├── .github └── workflows │ └── main.yml ├── .gitignore ├── .npmrc ├── .nvmrc ├── LICENSE ├── README.md ├── docs ├── 0d7535f482dc6111d4c1.bundle.js ├── 0d7535f482dc6111d4c1.bundle.js.LICENSE.txt ├── 0d7535f482dc6111d4c1.bundle.js.map ├── 1002a579d6bd0465baea.bundle.js ├── 1002a579d6bd0465baea.bundle.js.map ├── 7c707cace1261a1591d6.bundle.js ├── 7c707cace1261a1591d6.bundle.js.map ├── index.html └── src │ ├── style.css │ └── style.css.map ├── files ├── p2p-todo-demo.gif └── p2p-todo-demo.webm ├── package.json ├── renovate.json ├── src ├── database.ts ├── index.html ├── index.ts └── style.css ├── tsconfig.json └── webpack.config.js /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [master] 10 | pull_request: 11 | branches: [master] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | test: 19 | runs-on: ubuntu-24.04 20 | env: 21 | TAG: "${{github.sha}}" 22 | NODE_OPTIONS: "--max-old-space-size=4096" 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | - name: Set node version 27 | uses: actions/setup-node@v4 28 | with: 29 | node-version-file: ".nvmrc" 30 | 31 | - run: npm install 32 | - run: npm run build 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | log.txt 4 | package-lock.json 5 | *-debug.log 6 | .vscode 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | unsafe-perm = true 2 | package-lock=false 3 | save-exact=true 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.16.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Local-First peer-to-peer replicated todo list with RxDB and WebRTC 2 | 3 | This is a [local first](https://rxdb.info/offline-first.html) todo app that stores data locally with [RxDB](https://rxdb.info/) and replicates it [peer-to-peer with WebRTC](https://rxdb.info/replication-webrtc.html) to other devices without sending the data throught any central server. 4 | 5 | The whole app is implemented without a framework in about 200 lines of TypeScript code. To learn more about how it works, I recommend looking at the [source code](./src/index.ts) and read the [Quickstart Guide](https://rxdb.info/quickstart.html). 6 | 7 | ### Try live demo 8 | 9 | The app is deployed with github pages at [https://pubkey.github.io/rxdb-quickstart/](https://pubkey.github.io/rxdb-quickstart/). It will automatically assign you a room id as url hash. Open that url in another browser/device/tab to test the replication. 10 | 11 | ![p2p-todo-demo](./files/p2p-todo-demo.gif) 12 | 13 | ### Start the app locally (requires Node.js v20 installed): 14 | 15 | - Fork&Clone the repository. 16 | - Run `npm install` to install the npm dependencies. 17 | - Run `npm run dev` to start the webpack dev server and leave it open. 18 | - Open [http://localhost:8080/](http://localhost:8080/) in your browser. 19 | -------------------------------------------------------------------------------- /docs/0d7535f482dc6111d4c1.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * The buffer module from node.js, for the browser. 3 | * 4 | * @author Feross Aboukhadijeh 5 | * @license MIT 6 | */ 7 | 8 | /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh */ 9 | 10 | /*! queue-microtask. MIT License. Feross Aboukhadijeh */ 11 | 12 | /*! safe-buffer. MIT License. Feross Aboukhadijeh */ 13 | 14 | /*! simple-peer. MIT License. Feross Aboukhadijeh */ 15 | -------------------------------------------------------------------------------- /docs/1002a579d6bd0465baea.bundle.js: -------------------------------------------------------------------------------- 1 | "use strict";(self.webpackChunkrxdb_quickstart=self.webpackChunkrxdb_quickstart||[]).push([[761],{7761:(e,t,a)=>{a.r(t),a.d(t,{DEV_MODE_PLUGIN_NAME:()=>W,RxDBDevModePlugin:()=>$,areSelectorsSatisfiedByIndex:()=>N,checkFieldNameRegex:()=>w,checkMangoQuery:()=>U,checkPrimaryKey:()=>x,checkQuery:()=>P,checkSchema:()=>O,deepFreezeWhenDevMode:()=>J,disableWarnings:()=>F,ensureCollectionNameValid:()=>R,ensureDatabaseNameIsValid:()=>k,ensureObjectDoesNotContainRegExp:()=>T,isQueryAllowed:()=>_,validateDatabaseName:()=>Q,validateFieldsDeep:()=>g});var r,n,o=a(9299),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",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:"Malformated 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#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker ",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 this https://rxdb.info/questions-answers.html?console=qa#cant-change-the-schema ",DB8:"createRxDatabase(): A RxDatabase with the same name and adapter already exists.\nMake sure to use this combination only once or set ignoreDuplicate to true if you do this intentional-\nThis often happens in 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 "+o.Vx+". If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html#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",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",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",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",RM1:"Cannot communicate with a remote that was build on a different RxDB version. Did you forget to rebuild your workers when updating RxDB?",SNH:"This should never happen"},s=a(6606),c=a(6143),l=a(4815),h=a(2217),d=a(2085),m=a(9101),u=a(3704),p=a(5131),f=a(2497);var y,b=new((0,f.cM)(f.ky));function C(){if(!y){var e=Object.getOwnPropertyNames(b),t=Object.getOwnPropertyNames(f.ky);y=[...e,...t,"deleted","synced"]}return y}function w(e){if("_deleted"!==e){if(["properties"].includes(e))throw(0,s.Br)("SC23",{fieldName:e});var t="^[a-zA-Z](?:[[a-zA-Z0-9_]*]?[a-zA-Z0-9])?$",a=new RegExp(t);if("_id"!==e&&!e.match(a))throw(0,s.Br)("SC1",{regex:t,fieldName:e})}}function g(e){var t=(0,c.xQ)(e.primaryKey);return function e(a,r){a&&"object"==typeof a&&Object.keys(a).forEach((n=>{var o=a[n];a.properties||!o||"object"!=typeof o||Array.isArray(a)||function(e,a,r){if("string"!=typeof e||"object"!=typeof a||Array.isArray(a)||"patternProperties"===r.split(".").pop()||w(e),Object.prototype.hasOwnProperty.call(a,"item")&&"array"!==a.type)throw(0,s.Br)("SC2",{fieldName:e});if(Object.prototype.hasOwnProperty.call(a,"required")&&"boolean"==typeof a.required)throw(0,s.Br)("SC24",{fieldName:e});if(Object.prototype.hasOwnProperty.call(a,"$ref"))throw(0,s.Br)("SC40",{fieldName:e});if(Object.prototype.hasOwnProperty.call(a,"ref"))if(Array.isArray(a.type)){if(a.type.length>2||!a.type.includes("string")||!a.type.includes("null"))throw(0,s.Br)("SC4",{fieldName:e})}else switch(a.type){case"string":break;case"array":if(!a.items||!a.items.type||"string"!==a.items.type)throw(0,s.Br)("SC3",{fieldName:e});break;default:throw(0,s.Br)("SC4",{fieldName:e})}var n=r.split(".").length>=2;if(n&&a.default)throw(0,s.Br)("SC7",{path:r});if(!n){if("_id"===e&&"_id"!==t)throw(0,s.Br)("COL2",{fieldName:e});if("_"===e.charAt(0)){if("_id"===e||"_deleted"===e)return;throw(0,s.Br)("SC8",{fieldName:e})}}}(n,o,r);var i=r;"properties"!==n&&(i=i+"."+n),e(o,i)}))}(e,""),!0}function x(e){if(!e.primaryKey)throw(0,s.Br)("SC30",{schema:e});function t(t){if(!t)throw(0,s.Br)("SC33",{schema:e});var a=t.type;if(!a||!["string","number","integer"].includes(a))throw(0,s.Br)("SC32",{schema:e,args:{schemaPart:t}})}if("string"==typeof e.primaryKey){var a=e.primaryKey;t(e.properties[a])}else{var r=e.primaryKey;t((0,c.nT)(e,r.key)),r.fields.forEach((a=>{t((0,c.nT)(e,a))}))}var n=(0,c.xQ)(e.primaryKey),o=e.properties[n];if(!o.maxLength)throw(0,s.Br)("SC39",{schema:e,args:{primaryPathSchemaPart:o}});if(!isFinite(o.maxLength))throw(0,s.Br)("SC41",{schema:e,args:{primaryPathSchemaPart:o}})}function v(e){for(var t=e.split("."),a="",r=0;r{var a=e.properties[t];if(t===e.primaryKey){if(e.indexes&&e.indexes.includes(t))throw(0,s.Br)("SC13",{value:a,schema:e});if(a.unique)throw(0,s.Br)("SC14",{value:a,schema:e});if(e.encrypted&&e.encrypted.includes(t))throw(0,s.Br)("SC15",{value:a,schema:e});if("string"!==a.type)throw(0,s.Br)("SC16",{value:a,schema:e})}if(C().includes(t))throw(0,s.Br)("SC17",{key:t,schema:e})})),e.indexes){if(!(0,h.k_)(e.indexes))throw(0,s.Br)("SC18",{indexes:e.indexes,schema:e});e.indexes.forEach((t=>{if("string"!=typeof t&&!Array.isArray(t))throw(0,s.Br)("SC19",{index:t,schema:e});if(Array.isArray(t))for(var a=0;a{var r=(0,c.nT)(e,a);switch(r.type){case"string":if(!r.maxLength)throw(0,s.Br)("SC34",{index:t,field:a,schema:e});break;case"number":case"integer":if(!r.multipleOf)throw(0,s.Br)("SC35",{index:t,field:a,schema:e});var n=r.maximum,o=r.minimum;if(void 0===n||void 0===o)throw(0,s.Br)("SC37",{index:t,field:a,schema:e});if(!isFinite(n)||!isFinite(o))throw(0,s.Br)("SC41",{index:t,field:a,schema:e});break;case"boolean":var i="",l=a;if(a.includes(".")){var h=a.split(".");l=h.pop(),i=h.join(".")}var d=""===i?e:(0,c.nT)(e,i);if(!d.required||!d.required.includes(l))throw(0,s.Br)("SC38",{index:t,field:a,schema:e});break;default:throw(0,s.Br)("SC36",{fieldName:a,type:r.type,schema:e})}}))}))}Object.keys((0,d.YO)(e)).map((e=>{var t=e.split(".");return t.pop(),t.join(".")})).filter((e=>""!==e)).filter(((e,t,a)=>a.indexOf(e)===t)).filter((t=>{var a=(0,m.UU)(e,t);return a&&!!a.index})).forEach((t=>{throw t=(t=t.replace("properties.","")).replace(/\.properties\./g,"."),(0,s.Br)("SC26",{index:(0,l.L$)(t),schema:e})})),(e.indexes||[]).reduce(((e,t)=>((0,h.k_)(t)?(0,h.Hb)(e,t):e.push(t),e)),[]).filter(((e,t,a)=>a.indexOf(e)===t)).map((t=>{var a=v(t),r=(0,m.UU)(e,a);if(!r||"object"!=typeof r)throw(0,s.Br)("SC21",{index:t,schema:e});return{indexPath:t,schemaObj:r}})).filter((e=>"string"!==e.schemaObj.type&&"integer"!==e.schemaObj.type&&"number"!==e.schemaObj.type&&"boolean"!==e.schemaObj.type)).forEach((t=>{throw(0,s.Br)("SC22",{key:t.indexPath,type:t.schemaObj.type,schema:e})})),e.encrypted&&e.encrypted.forEach((t=>{var a=v(t),r=(0,m.UU)(e,a);if(!r||"object"!=typeof r)throw(0,s.Br)("SC28",{field:t,schema:e})}))}function D(e){e&&Object.entries(e).forEach((([e,t])=>{if("string"!=typeof e)throw(0,s.JX)("COL14",{name:e});if(e.startsWith("_"))throw(0,s.JX)("COL15",{name:e});if("function"!=typeof t)throw(0,s.JX)("COL16",{name:e,type:typeof e});if(function(){if(!r){var e=new u.wm,t=Object.getOwnPropertyNames(e),a=Object.getOwnPropertyNames(Object.getPrototypeOf(e));r=[...t,...a]}return r}().includes(e)||C().includes(e))throw(0,s.Br)("COL17",{name:e})}))}var S=a(3907);function R(e){if(function(){if(!n){var e=new p.lB("pseudoInstance","memory"),t=Object.getOwnPropertyNames(e),a=Object.getOwnPropertyNames(Object.getPrototypeOf(e));n=[...t,...a],e.close()}return n}().includes(e.name))throw(0,s.Br)("DB5",{name:e.name});Q(e.name)}function k(e){if(Q(e.name),e.name.includes("$"))throw(0,s.Br)("DB13",{name:e.name});if((0,l.ky)(e.name)&&(e.name.endsWith("/")||e.name.endsWith("\\")))throw(0,s.Br)("DB11",{name:e.name})}var B="^[a-z][_$a-z0-9\\-]*$",j=new RegExp(B);function Q(e){if("string"!=typeof e||0===e.length)throw(0,s.JX)("UT1",{name:e});if((0,l.ky)(e))return!0;if(!e.match(j)&&":memory:"!==e)throw(0,s.Br)("UT2",{regex:B,givenName:e});return!0}var L=a(4701),q=a(5014);function P(e){if(!("[object Object]"===Object.prototype.toString.call(e.queryObj)))throw(0,s.JX)("QU11",{op:e.op,collection:e.collection.name,queryObj:e.queryObj});var t=["selector","limit","skip","sort","index"];if(Object.keys(e.queryObj).forEach((a=>{if(!t.includes(a))throw(0,s.JX)("QU11",{op:e.op,collection:e.collection.name,queryObj:e.queryObj,key:a,args:{validKeys:t}})})),"count"===e.op&&(e.queryObj.limit||e.queryObj.skip))throw(0,s.Br)("QU15",{collection:e.collection.name,query:e.queryObj});T(e.queryObj)}function U(e){var t=e.rxQuery.collection.schema.jsonSchema,a=e.mangoQuery.selector,r=Object.keys(t.properties);Object.keys(a).filter((e=>!e.startsWith("$"))).filter((e=>!e.includes("."))).forEach((a=>{if(!r.includes(a))throw(0,s.Br)("QU13",{schema:t,field:a,query:e.mangoQuery})}));var n=t.indexes?t.indexes:[],o=e.mangoQuery.index;if(o&&!n.find((e=>(0,L.b)(e,o))))throw(0,s.Br)("QU12",{collection:e.rxQuery.collection.name,query:e.mangoQuery,schema:t});if("count"===e.rxQuery.op&&!N(e.rxQuery.collection.schema.jsonSchema,e.mangoQuery)&&!e.rxQuery.collection.database.allowSlowCount)throw(0,s.Br)("QU14",{collection:e.rxQuery.collection,query:e.mangoQuery});e.mangoQuery.sort&&e.mangoQuery.sort.map((e=>Object.keys(e)[0])).filter((e=>!e.includes("."))).forEach((a=>{if(!r.includes(a))throw(0,s.Br)("QU13",{schema:t,field:a,query:e.mangoQuery})})),T(e.mangoQuery)}function N(e,t){return(0,q.Ib)(e,t).queryPlan.selectorSatisfiedByIndex}function T(e){"object"==typeof e&&null!==e&&Object.keys(e).forEach((t=>{var a=e[t];if(a instanceof RegExp)throw(0,s.Br)("QU16",{field:t,query:e});Array.isArray(a)?a.forEach((e=>T(e))):T(a)}))}function _(e){if("findOne"===e.op){if("number"==typeof e.queryObj||Array.isArray(e.queryObj))throw(0,s.JX)("COL6",{collection:e.collection.name,queryObj:e.queryObj})}else if("find"===e.op&&"string"==typeof e.queryObj)throw(0,s.Br)("COL5",{collection:e.collection.name,queryObj:e.queryObj})}function E(e){if("object"!=typeof e||null===e)return!1;for(var t in e)if(e.hasOwnProperty(t)){if(e[t]instanceof Date)return!0;if("object"==typeof e[t]&&E(e[t]))return!0}return!1}function M(e,t){var a=(0,c.xQ)(e.schema.primaryKey),r=function(t){t.document=(0,c.Nq)(a,e.schema,t.document),t.previous&&Object.keys(t.previous._meta).forEach((e=>{if(!Object.prototype.hasOwnProperty.call(t.document._meta,e))throw(0,s.Br)("SNH",{dataBefore:t.previous,dataAfter:t.document,args:{metaFieldName:e}})}));try{"function"==typeof structuredClone?structuredClone(t):JSON.parse(JSON.stringify(t))}catch(a){throw(0,s.Br)("DOC24",{collection:e.collectionName,document:t.document})}if(E(t.document))throw(0,s.Br)("DOC24",{collection:e.collectionName,document:t.document})};for(var n of t)r(n)}var A=a(4807),I=!1;var K=!0;function F(){K=!1}function J(e){return e&&"string"!=typeof e&&"number"!=typeof e?(0,d.ol)(e):e}var W="dev-mode",$={name:W,rxdb:!0,init:()=>{K&&console.warn(["-------------- RxDB dev-mode warning -------------------------------","you are seeing this because you use the RxDB dev-mode plugin https://rxdb.info/dev-mode.html?console=dev-mode ","This is great in development mode, because it will run many checks to ensure","that you use RxDB correct. If you see this in production mode,","you did something wrong because the dev-mode plugin will decrease the performance.","","🤗 Hint: To get the most out of RxDB, check out the Premium Plugins","to get access to faster storages and more professional features: https://rxdb.info/premium?console=dev-mode ","","You can disable this warning by calling disableWarnings() from the dev-mode plugin.","---------------------------------------------------------------------"].join("\n"))},overwritable:{isDevMode:()=>!0,deepFreezeWhenDevMode:J,tunnelErrorMessage(e){if(!i[e])throw console.error("RxDB: Error-Code not known: "+e),new Error("Error-Code "+e+" not known, contact the maintainer");return i[e]}},hooks:{preCreateRxSchema:{after:O},preCreateRxDatabase:{before:function(e){if(!e.storage.name.startsWith("validate-"))throw(0,s.Br)("DVM1",{database:e.name,storage:e.storage.name})},after:function(e){k(e)}},createRxDatabase:{after:async function(e){!async function(){if(!I&&"undefined"!=typeof window&&"undefined"!=typeof location&&!await(0,o._H)()){I=!0;var e=document.createElement("iframe");e.style.visibility="hidden",e.width="1px",e.height="1px",e.style.position="absolute",e.style.top="0",e.style.left="0",e.style.opacity="0.1",e.src="https://rxdb.info/html/dev-mode-iframe.html?version="+A.o,document.body.appendChild(e)}}(e.database)}},preCreateRxCollection:{after:function(e){var t,a,r;if(R(e),t=e.schema,a=e.methods,r=Object.keys(t.properties),a&&Object.keys(a).filter((e=>r.includes(e))).forEach((e=>{throw(0,s.Br)("COL18",{funName:e})})),"_"===e.name.charAt(0))throw(0,s.Br)("DB2",{name:e.name});if(!e.schema)throw(0,s.Br)("DB4",{name:e.name,args:e})}},createRxDocument:{before:function(e){!function(e,t){if(!e)throw(0,s.Br)("DOC20",{primaryKey:e,document:t});if(e!==e.trim())throw(0,s.Br)("DOC21",{primaryKey:e,document:t});if(e.includes("\r")||e.includes("\n"))throw(0,s.Br)("DOC22",{primaryKey:e,document:t});if(e.includes('"'))throw(0,s.Br)("DOC23",{primaryKey:e,document:t})}(e.primary,e.toJSON(!0))}},prePrepareRxQuery:{after:function(e){_(e)}},preCreateRxQuery:{after:function(e){P(e)}},prePrepareQuery:{after:e=>{U(e)}},preStorageWrite:{before:e=>{M(e.storageInstance,e.rows)}},createRxCollection:{after:e=>{D(e.creator.statics),D(e.creator.methods),D(e.creator.attachments),e.creator.schema&&e.creator.migrationStrategies&&function(e,t){if("object"!=typeof t||Array.isArray(t))throw(0,s.JX)("COL11",{schema:e});var a=(0,S.NP)(e);if(a.length!==Object.keys(t).length)throw(0,s.Br)("COL12",{have:Object.keys(t),should:a});a.map((e=>({v:e,s:t[e+1]}))).filter((e=>"function"!=typeof e.s)).forEach((t=>{throw(0,s.JX)("COL13",{version:t.v,type:typeof t,schema:e})}))}(e.creator.schema,e.creator.migrationStrategies)}}}}}}]); 2 | //# sourceMappingURL=1002a579d6bd0465baea.bundle.js.map -------------------------------------------------------------------------------- /docs/1002a579d6bd0465baea.bundle.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"1002a579d6bd0465baea.bundle.js","mappings":"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","sources":["webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/entity-properties.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/error-messages.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/check-schema.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/check-orm.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/unallowed-properties.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/check-query.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/check-document.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/dev-mode-tracking.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/index.js","webpack://rxdb-quickstart/./node_modules/rxdb/dist/esm/plugins/dev-mode/check-migration-strategies.js"],"sourcesContent":["import { RxCollectionBase } from \"../../rx-collection.js\";\nimport { RxDatabaseBase } from \"../../rx-database.js\";\nimport { createRxDocumentConstructor, basePrototype } from \"../../rx-document.js\";\n\n/**\n * returns all possible properties of a RxCollection-instance\n */\nvar _rxCollectionProperties;\nexport function rxCollectionProperties() {\n if (!_rxCollectionProperties) {\n var pseudoInstance = new RxCollectionBase();\n var ownProperties = Object.getOwnPropertyNames(pseudoInstance);\n var prototypeProperties = Object.getOwnPropertyNames(Object.getPrototypeOf(pseudoInstance));\n _rxCollectionProperties = [...ownProperties, ...prototypeProperties];\n }\n return _rxCollectionProperties;\n}\n\n/**\n * returns all possible properties of a RxDatabase-instance\n */\nvar _rxDatabaseProperties;\nexport function rxDatabaseProperties() {\n if (!_rxDatabaseProperties) {\n var pseudoInstance = new RxDatabaseBase('pseudoInstance', 'memory');\n var ownProperties = Object.getOwnPropertyNames(pseudoInstance);\n var prototypeProperties = Object.getOwnPropertyNames(Object.getPrototypeOf(pseudoInstance));\n _rxDatabaseProperties = [...ownProperties, ...prototypeProperties];\n pseudoInstance.close();\n }\n return _rxDatabaseProperties;\n}\n\n/**\n * returns all possible properties of a RxDocument\n */\nvar pseudoConstructor = createRxDocumentConstructor(basePrototype);\nvar pseudoRxDocument = new pseudoConstructor();\nvar _rxDocumentProperties;\nexport function rxDocumentProperties() {\n if (!_rxDocumentProperties) {\n var reserved = ['deleted', 'synced'];\n var ownProperties = Object.getOwnPropertyNames(pseudoRxDocument);\n var prototypeProperties = Object.getOwnPropertyNames(basePrototype);\n _rxDocumentProperties = [...ownProperties, ...prototypeProperties, ...reserved];\n }\n return _rxDocumentProperties;\n}\n//# sourceMappingURL=entity-properties.js.map","/**\n * this plugin adds the error-messages\n * without it, only error-codes will be shown\n * This is mainly because error-string are hard to compress and we need a smaller build\n */\n\nimport { NON_PREMIUM_COLLECTION_LIMIT } from \"../utils/utils-premium.js\";\nexport var ERROR_MESSAGES = {\n // util.js / config\n UT1: 'given name is no string or empty',\n 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 '\\\\'\",\n UT3: 'replication-direction must either be push or pull or both. But not none',\n UT4: 'given leveldown is no valid adapter',\n UT5: 'keyCompression is set to true in the schema but no key-compression handler is used in the storage',\n UT6: 'schema contains encrypted fields but no encryption handler is used in the storage',\n UT7: 'attachments.compression is enabled but no attachment-compression plugin is used',\n // plugins\n PL1: 'Given plugin is not RxDB plugin.',\n // removed in 14.0.0 - PouchDB RxStorage was removed - PL2: 'You tried importing a RxDB plugin to pouchdb. Use addRxPlugin() instead.',\n PL3: 'A plugin with the same name was already added but it was not the exact same JavaScript object',\n // pouch-db.js\n // removed in 12.0.0 - P1: 'PouchDB.getBatch: limit must be > 2',\n P2: 'bulkWrite() cannot be called with an empty array',\n // removed in 12.0.0 - P3: 'bulkAddRevisions cannot be called with an empty array',\n\n // rx-query\n QU1: 'RxQuery._execOverDatabase(): op not known',\n // removed in 9.0.0 - QU2: 'limit() must get a number',\n // removed in 9.0.0 - QU3: 'skip() must get a number',\n QU4: 'RxQuery.regex(): You cannot use .regex() on the primary field',\n QU5: 'RxQuery.sort(): does not work because key is not defined in the schema',\n QU6: 'RxQuery.limit(): cannot be called on .findOne()',\n // removed in 12.0.0 (should by ensured by the typings) - QU7: 'query must be an object',\n // removed in 12.0.0 (should by ensured by the typings) - QU8: 'query cannot be an array',\n QU9: 'throwIfMissing can only be used in findOne queries',\n QU10: 'result empty and throwIfMissing: true',\n QU11: 'RxQuery: no valid query params given',\n QU12: 'Given index is not in schema',\n QU13: 'A top level field of the query is not included in the schema',\n 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',\n QU15: 'For count queries it is not allowed to use skip or limit',\n 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',\n QU17: 'Chained queries cannot be used on findByIds() RxQuery instances',\n QU18: 'Malformated 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#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker ',\n // mquery.js\n MQ1: 'path must be a string or object',\n MQ2: 'Invalid argument',\n MQ3: 'Invalid sort() argument. Must be a string, object, or array',\n MQ4: 'Invalid argument. Expected instanceof mquery or plain object',\n MQ5: 'method must be used after where() when called with these arguments',\n MQ6: 'Can\\'t mix sort syntaxes. Use either array or object | .sort([[\\'field\\', 1], [\\'test\\', -1]]) | .sort({ field: 1, test: -1 })',\n MQ7: 'Invalid sort value',\n MQ8: 'Can\\'t mix sort syntaxes. Use either array or object',\n // rx-database\n DB1: 'RxDocument.prepare(): another instance on this adapter has a different password',\n DB2: 'RxDatabase.addCollections(): collection-names cannot start with underscore _',\n DB3: 'RxDatabase.addCollections(): collection already exists. use myDatabase[collectionName] to get it',\n DB4: 'RxDatabase.addCollections(): schema is missing',\n DB5: 'RxDatabase.addCollections(): collection-name not allowed',\n DB6: 'RxDatabase.addCollections(): another instance created this collection with a different schema. Read this https://rxdb.info/questions-answers.html?console=qa#cant-change-the-schema ',\n // removed in 13.0.0 (now part of the encryption plugin) DB7: 'RxDatabase.addCollections(): schema encrypted but no password given',\n DB8: 'createRxDatabase(): A RxDatabase with the same name and adapter already exists.\\n' + 'Make sure to use this combination only once or set ignoreDuplicate to true if you do this intentional-\\n' + 'This often happens in react projects with hot reload that reloads the code without reloading the process.',\n DB9: 'ignoreDuplicate is only allowed in dev-mode and must never be used in production',\n // removed in 14.0.0 - PouchDB RxStorage is removed - DB9: 'createRxDatabase(): Adapter not added. Use addPouchPlugin(require(\\'pouchdb-adapter-[adaptername]\\'));',\n // removed in 14.0.0 - PouchDB RxStorage is removed DB10: 'createRxDatabase(): To use leveldown-adapters, you have to add the leveldb-plugin. Use addPouchPlugin(require(\\'pouchdb-adapter-leveldb\\'));',\n DB11: 'createRxDatabase(): Invalid db-name, folder-paths must not have an ending slash',\n DB12: 'RxDatabase.addCollections(): could not write to internal store',\n DB13: 'createRxDatabase(): Invalid db-name or collection name, name contains the dollar sign',\n DB14: 'no custom reactivity factory added on database creation',\n // rx-collection\n COL1: 'RxDocument.insert() You cannot insert an existing document',\n COL2: 'RxCollection.insert() fieldName ._id can only be used as primaryKey',\n COL3: 'RxCollection.upsert() does not work without primary',\n COL4: 'RxCollection.incrementalUpsert() does not work without primary',\n COL5: 'RxCollection.find() if you want to search by _id, use .findOne(_id)',\n COL6: 'RxCollection.findOne() needs a queryObject or string. Notice that in RxDB, primary keys must be strings and cannot be numbers.',\n COL7: 'hook must be a function',\n COL8: 'hooks-when not known',\n COL9: 'RxCollection.addHook() hook-name not known',\n COL10: 'RxCollection .postCreate-hooks cannot be async',\n COL11: 'migrationStrategies must be an object',\n COL12: 'A migrationStrategy is missing or too much',\n COL13: 'migrationStrategy must be a function',\n COL14: 'given static method-name is not a string',\n COL15: 'static method-names cannot start with underscore _',\n COL16: 'given static method is not a function',\n COL17: 'RxCollection.ORM: statics-name not allowed',\n COL18: 'collection-method not allowed because fieldname is in the schema',\n // removed in 14.0.0, use CONFLICT instead - COL19: 'Document update conflict. When changing a document you must work on the previous revision',\n COL20: 'Storage write error',\n COL21: 'The RxCollection is closed or removed already, either from this JavaScript realm or from another, like a browser tab',\n CONFLICT: 'Document update conflict. When changing a document you must work on the previous revision',\n COL22: '.bulkInsert() and .bulkUpsert() cannot be run with multiple documents that have the same primary key',\n COL23: 'In the open-source version of RxDB, the amount of collections that can exist in parallel is limited to ' + NON_PREMIUM_COLLECTION_LIMIT + '. If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html#faq',\n // rx-document.js\n DOC1: 'RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed',\n DOC2: 'cannot observe primary path',\n DOC3: 'final fields cannot be observed',\n DOC4: 'RxDocument.get$ cannot observe a non-existed field',\n DOC5: 'RxDocument.populate() cannot populate a non-existed field',\n DOC6: 'RxDocument.populate() cannot populate because path has no ref',\n DOC7: 'RxDocument.populate() ref-collection not in database',\n DOC8: 'RxDocument.set(): primary-key cannot be modified',\n DOC9: 'final fields cannot be modified',\n DOC10: 'RxDocument.set(): cannot set childpath when rootPath not selected',\n DOC11: 'RxDocument.save(): can\\'t save deleted document',\n // removed in 10.0.0 DOC12: 'RxDocument.save(): error',\n DOC13: 'RxDocument.remove(): Document is already deleted',\n DOC14: 'RxDocument.close() does not exist',\n DOC15: 'query cannot be an array',\n DOC16: 'Since version 8.0.0 RxDocument.set() can only be called on temporary RxDocuments',\n DOC17: 'Since version 8.0.0 RxDocument.save() can only be called on non-temporary documents',\n DOC18: 'Document property for composed primary key is missing',\n DOC19: 'Value of primary key(s) cannot be changed',\n DOC20: 'PrimaryKey missing',\n DOC21: 'PrimaryKey must be equal to PrimaryKey.trim(). It cannot start or end with a whitespace',\n DOC22: 'PrimaryKey must not contain a linebreak',\n DOC23: 'PrimaryKey must not contain a double-quote [\"]',\n 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.',\n // data-migrator.js\n DM1: 'migrate() Migration has already run',\n DM2: 'migration of document failed final document does not match final schema',\n DM3: 'migration already running',\n DM4: 'Migration errored',\n 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 ',\n // plugins/attachments.js\n AT1: 'to use attachments, please define this in your schema',\n // plugins/encryption-crypto-js.js\n EN1: 'password is not valid',\n EN2: 'validatePassword: min-length of password not complied',\n EN3: 'Schema contains encrypted properties but no password is given',\n EN4: 'Password not valid',\n // plugins/json-dump.js\n JD1: 'You must create the collections before you can import their data',\n JD2: 'RxCollection.importJSON(): the imported json relies on a different schema',\n JD3: 'RxCollection.importJSON(): json.passwordHash does not match the own',\n // plugins/leader-election.js\n\n // plugins/local-documents.js\n LD1: 'RxDocument.allAttachments$ can\\'t use attachments on local documents',\n LD2: 'RxDocument.get(): objPath must be a string',\n LD3: 'RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed',\n LD4: 'cannot observe primary path',\n LD5: 'RxDocument.set() id cannot be modified',\n LD6: 'LocalDocument: Function is not usable on local documents',\n LD7: 'Local document already exists',\n LD8: 'localDocuments not activated. Set localDocuments=true on creation, when you want to store local documents on the RxDatabase or RxCollection.',\n // plugins/replication.js\n RC1: 'Replication: already added',\n RC2: 'replicateCouchDB() query must be from the same RxCollection',\n // removed in 14.0.0 - PouchDB RxStorage is removed RC3: 'RxCollection.syncCouchDB() Do not use a collection\\'s pouchdb as remote, use the collection instead',\n RC4: 'RxCouchDBReplicationState.awaitInitialReplication() cannot await initial replication when live: true',\n RC5: 'RxCouchDBReplicationState.awaitInitialReplication() cannot await initial replication if multiInstance because the replication might run on another instance',\n RC6: 'syncFirestore() serverTimestampField MUST NOT be part of the collections schema and MUST NOT be nested.',\n RC7: 'SimplePeer requires to have process.nextTick() polyfilled, see https://rxdb.info/replication-webrtc.html?console=webrtc ',\n RC_PULL: 'RxReplication pull handler threw an error - see .errors for more details',\n RC_STREAM: 'RxReplication pull stream$ threw an error - see .errors for more details',\n RC_PUSH: 'RxReplication push handler threw an error - see .errors for more details',\n RC_PUSH_NO_AR: 'RxReplication push handler did not return an array with the conflicts',\n RC_WEBRTC_PEER: 'RxReplication WebRTC Peer has error',\n RC_COUCHDB_1: 'replicateCouchDB() url must end with a slash like \\'https://example.com/mydatabase/\\'',\n RC_COUCHDB_2: 'replicateCouchDB() did not get valid result with rows.',\n RC_OUTDATED: 'Outdated client, update required. Replication was canceled',\n RC_UNAUTHORIZED: 'Unauthorized client, update the replicationState.headers to set correct auth data',\n 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',\n // plugins/dev-mode/check-schema.js\n SC1: 'fieldnames do not match the regex',\n SC2: 'SchemaCheck: name \\'item\\' reserved for array-fields',\n SC3: 'SchemaCheck: fieldname has a ref-array but items-type is not string',\n SC4: 'SchemaCheck: fieldname has a ref but is not type string, [string,null] or array',\n SC6: 'SchemaCheck: primary can only be defined at top-level',\n SC7: 'SchemaCheck: default-values can only be defined at top-level',\n SC8: 'SchemaCheck: first level-fields cannot start with underscore _',\n SC10: 'SchemaCheck: schema defines ._rev, this will be done automatically',\n SC11: 'SchemaCheck: schema needs a number >=0 as version',\n // removed in 10.0.0 - SC12: 'SchemaCheck: primary can only be defined once',\n SC13: 'SchemaCheck: primary is always index, do not declare it as index',\n SC14: 'SchemaCheck: primary is always unique, do not declare it as index',\n SC15: 'SchemaCheck: primary cannot be encrypted',\n SC16: 'SchemaCheck: primary must have type: string',\n SC17: 'SchemaCheck: top-level fieldname is not allowed',\n SC18: 'SchemaCheck: indexes must be an array',\n SC19: 'SchemaCheck: indexes must contain strings or arrays of strings',\n SC20: 'SchemaCheck: indexes.array must contain strings',\n SC21: 'SchemaCheck: given index is not defined in schema',\n SC22: 'SchemaCheck: given indexKey is not type:string',\n SC23: 'SchemaCheck: fieldname is not allowed',\n SC24: 'SchemaCheck: required fields must be set via array. See https://spacetelescope.github.io/understanding-json-schema/reference/object.html#required',\n SC25: 'SchemaCheck: compoundIndexes needs to be specified in the indexes field',\n SC26: 'SchemaCheck: indexes needs to be specified at collection schema level',\n // removed in 16.0.0 - SC27: 'SchemaCheck: encrypted fields need to be specified at collection schema level',\n SC28: 'SchemaCheck: encrypted fields is not defined in the schema',\n SC29: 'SchemaCheck: missing object key \\'properties\\'',\n SC30: 'SchemaCheck: primaryKey is required',\n SC32: 'SchemaCheck: primary field must have the type string/number/integer',\n SC33: 'SchemaCheck: used primary key is not a property in the schema',\n SC34: 'Fields of type string that are used in an index, must have set the maxLength attribute in the schema',\n SC35: 'Fields of type number/integer that are used in an index, must have set the multipleOf attribute in the schema',\n SC36: 'A field of this type cannot be used as index',\n SC37: 'Fields of type number that are used in an index, must have set the minimum and maximum attribute in the schema',\n SC38: 'Fields of type boolean that are used in an index, must be required in the schema',\n SC39: 'The primary key must have the maxLength attribute set',\n 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.',\n SC41: 'minimum, maximum and maxLength values for indexes must be real numbers, not Infinity or -Infinity',\n // plugins/dev-mode\n // removed in 13.9.0, use PL3 instead - DEV1: 'dev-mode added multiple times',\n 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.',\n // plugins/validate.js\n VD1: 'Sub-schema not found, does the schemaPath exists in your schema?',\n VD2: 'object does not match schema',\n // plugins/in-memory.js\n // removed in 14.0.0 - PouchDB RxStorage is removed IM1: 'InMemory: Memory-Adapter must be added. Use addPouchPlugin(require(\\'pouchdb-adapter-memory\\'));',\n // removed in 14.0.0 - PouchDB RxStorage is removed IM2: 'inMemoryCollection.sync(): Do not replicate with the in-memory instance. Replicate with the parent instead',\n\n // plugins/server.js\n S1: 'You cannot create collections after calling RxDatabase.server()',\n // plugins/replication-graphql.js\n GQL1: 'GraphQL replication: cannot find sub schema by key',\n // removed in 13.0.0, use RC_PULL instead - GQL2: 'GraphQL replication: unknown errors occurred in replication pull - see innerErrors for more details',\n GQL3: 'GraphQL replication: pull returns more documents then batchSize',\n // removed in 13.0.0, use RC_PUSH instead - GQL4: 'GraphQL replication: unknown errors occurred in replication push - see innerErrors for more details',\n\n // plugins/crdt/\n CRDT1: 'CRDT operations cannot be used because the crdt options are not set in the schema.',\n CRDT2: 'RxDocument.incrementalModify() cannot be used when CRDTs are activated.',\n CRDT3: 'To use CRDTs you MUST NOT set a conflictHandler because the default CRDT conflict handler must be used',\n // plugins/storage-dexie/\n DXE1: 'non-required index fields are not possible with the dexie.js RxStorage: https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082',\n // removed in 15.0.0, added boolean index support to dexie storage - DXE1: 'The dexie.js RxStorage does not support boolean indexes, see https://rxdb.info/rx-storage-dexie.html#boolean-index',\n\n // plugins/storage-remote\n RM1: 'Cannot communicate with a remote that was build on a different RxDB version. Did you forget to rebuild your workers when updating RxDB?',\n /**\n * Should never be thrown, use this for\n * null checks etc. so you do not have to increase the\n * build size with error message strings.\n */\n SNH: 'This should never happen'\n};\n//# sourceMappingURL=error-messages.js.map","/**\n * does additional checks over the schema-json\n * to ensure nothing is broken or not supported\n */\nimport { newRxError } from \"../../rx-error.js\";\nimport { getPrimaryFieldOfPrimaryKey, getSchemaByObjectPath } from \"../../rx-schema-helper.js\";\nimport { appendToArray, flattenObject, getProperty, isMaybeReadonlyArray, trimDots } from \"../../plugins/utils/index.js\";\nimport { rxDocumentProperties } from \"./entity-properties.js\";\n\n/**\n * checks if the fieldname is allowed\n * this makes sure that the fieldnames can be transformed into javascript-vars\n * and does not conquer the observe$ and populate_ fields\n * @throws {Error}\n */\nexport function checkFieldNameRegex(fieldName) {\n if (fieldName === '_deleted') {\n return;\n }\n if (['properties'].includes(fieldName)) {\n throw newRxError('SC23', {\n fieldName\n });\n }\n var regexStr = '^[a-zA-Z](?:[[a-zA-Z0-9_]*]?[a-zA-Z0-9])?$';\n var regex = new RegExp(regexStr);\n if (\n /**\n * It must be allowed to set _id as primaryKey.\n * This makes it sometimes easier to work with RxDB+CouchDB\n * @link https://github.com/pubkey/rxdb/issues/681\n */\n fieldName !== '_id' && !fieldName.match(regex)) {\n throw newRxError('SC1', {\n regex: regexStr,\n fieldName\n });\n }\n}\n\n/**\n * validate that all schema-related things are ok\n */\nexport function validateFieldsDeep(rxJsonSchema) {\n var primaryPath = getPrimaryFieldOfPrimaryKey(rxJsonSchema.primaryKey);\n function checkField(fieldName, schemaObj, path) {\n if (typeof fieldName === 'string' && typeof schemaObj === 'object' && !Array.isArray(schemaObj) && path.split('.').pop() !== 'patternProperties') checkFieldNameRegex(fieldName);\n\n // 'item' only allowed it type=='array'\n if (Object.prototype.hasOwnProperty.call(schemaObj, 'item') && schemaObj.type !== 'array') {\n throw newRxError('SC2', {\n fieldName\n });\n }\n\n /**\n * required fields cannot be set via 'required: true',\n * but must be set via required: []\n */\n if (Object.prototype.hasOwnProperty.call(schemaObj, 'required') && typeof schemaObj.required === 'boolean') {\n throw newRxError('SC24', {\n fieldName\n });\n }\n\n // $ref is not allowed\n if (Object.prototype.hasOwnProperty.call(schemaObj, '$ref')) {\n throw newRxError('SC40', {\n fieldName\n });\n }\n\n // if ref given, must be type=='string', type=='array' with string-items or type==['string','null']\n if (Object.prototype.hasOwnProperty.call(schemaObj, 'ref')) {\n if (Array.isArray(schemaObj.type)) {\n if (schemaObj.type.length > 2 || !schemaObj.type.includes('string') || !schemaObj.type.includes('null')) {\n throw newRxError('SC4', {\n fieldName\n });\n }\n } else {\n switch (schemaObj.type) {\n case 'string':\n break;\n case 'array':\n if (!schemaObj.items || !schemaObj.items.type || schemaObj.items.type !== 'string') {\n throw newRxError('SC3', {\n fieldName\n });\n }\n break;\n default:\n throw newRxError('SC4', {\n fieldName\n });\n }\n }\n }\n var isNested = path.split('.').length >= 2;\n\n // nested only\n if (isNested) {\n if (schemaObj.default) {\n throw newRxError('SC7', {\n path\n });\n }\n }\n\n // first level\n if (!isNested) {\n // if _id is used, it must be primaryKey\n if (fieldName === '_id' && primaryPath !== '_id') {\n throw newRxError('COL2', {\n fieldName\n });\n }\n\n // check underscore fields\n if (fieldName.charAt(0) === '_') {\n if (\n // exceptional allow underscore on these fields.\n fieldName === '_id' || fieldName === '_deleted') {\n return;\n }\n throw newRxError('SC8', {\n fieldName\n });\n }\n }\n }\n function traverse(currentObj, currentPath) {\n if (!currentObj || typeof currentObj !== 'object') {\n return;\n }\n Object.keys(currentObj).forEach(attributeName => {\n var schemaObj = currentObj[attributeName];\n if (!currentObj.properties && schemaObj && typeof schemaObj === 'object' && !Array.isArray(currentObj)) {\n checkField(attributeName, schemaObj, currentPath);\n }\n var nextPath = currentPath;\n if (attributeName !== 'properties') nextPath = nextPath + '.' + attributeName;\n traverse(schemaObj, nextPath);\n });\n }\n traverse(rxJsonSchema, '');\n return true;\n}\nexport function checkPrimaryKey(jsonSchema) {\n if (!jsonSchema.primaryKey) {\n throw newRxError('SC30', {\n schema: jsonSchema\n });\n }\n function validatePrimarySchemaPart(schemaPart) {\n if (!schemaPart) {\n throw newRxError('SC33', {\n schema: jsonSchema\n });\n }\n var type = schemaPart.type;\n if (!type || !['string', 'number', 'integer'].includes(type)) {\n throw newRxError('SC32', {\n schema: jsonSchema,\n args: {\n schemaPart\n }\n });\n }\n }\n if (typeof jsonSchema.primaryKey === 'string') {\n var key = jsonSchema.primaryKey;\n var schemaPart = jsonSchema.properties[key];\n validatePrimarySchemaPart(schemaPart);\n } else {\n var compositePrimaryKey = jsonSchema.primaryKey;\n var keySchemaPart = getSchemaByObjectPath(jsonSchema, compositePrimaryKey.key);\n validatePrimarySchemaPart(keySchemaPart);\n compositePrimaryKey.fields.forEach(field => {\n var schemaPart = getSchemaByObjectPath(jsonSchema, field);\n validatePrimarySchemaPart(schemaPart);\n });\n }\n\n /**\n * The primary key must have a maxLength set\n * which is required by some RxStorage implementations\n * to ensure we can craft custom index strings.\n */\n var primaryPath = getPrimaryFieldOfPrimaryKey(jsonSchema.primaryKey);\n var primaryPathSchemaPart = jsonSchema.properties[primaryPath];\n if (!primaryPathSchemaPart.maxLength) {\n throw newRxError('SC39', {\n schema: jsonSchema,\n args: {\n primaryPathSchemaPart\n }\n });\n } else if (!isFinite(primaryPathSchemaPart.maxLength)) {\n throw newRxError('SC41', {\n schema: jsonSchema,\n args: {\n primaryPathSchemaPart\n }\n });\n }\n}\n\n/**\n * computes real path of the object path in the collection schema\n */\nfunction getSchemaPropertyRealPath(shortPath) {\n var pathParts = shortPath.split('.');\n var realPath = '';\n for (var i = 0; i < pathParts.length; i += 1) {\n if (pathParts[i] !== '[]') {\n realPath = realPath.concat('.properties.'.concat(pathParts[i]));\n } else {\n realPath = realPath.concat('.items');\n }\n }\n return trimDots(realPath);\n}\n\n/**\n * does the checking\n * @throws {Error} if something is not ok\n */\nexport function checkSchema(jsonSchema) {\n if (!jsonSchema.primaryKey) {\n throw newRxError('SC30', {\n schema: jsonSchema\n });\n }\n if (!Object.prototype.hasOwnProperty.call(jsonSchema, 'properties')) {\n throw newRxError('SC29', {\n schema: jsonSchema\n });\n }\n\n // _rev MUST NOT exist, it is added by RxDB\n if (jsonSchema.properties._rev) {\n throw newRxError('SC10', {\n schema: jsonSchema\n });\n }\n\n // check version\n if (!Object.prototype.hasOwnProperty.call(jsonSchema, 'version') || typeof jsonSchema.version !== 'number' || jsonSchema.version < 0) {\n throw newRxError('SC11', {\n version: jsonSchema.version\n });\n }\n validateFieldsDeep(jsonSchema);\n checkPrimaryKey(jsonSchema);\n Object.keys(jsonSchema.properties).forEach(key => {\n var value = jsonSchema.properties[key];\n // check primary\n if (key === jsonSchema.primaryKey) {\n if (jsonSchema.indexes && jsonSchema.indexes.includes(key)) {\n throw newRxError('SC13', {\n value,\n schema: jsonSchema\n });\n }\n if (value.unique) {\n throw newRxError('SC14', {\n value,\n schema: jsonSchema\n });\n }\n if (jsonSchema.encrypted && jsonSchema.encrypted.includes(key)) {\n throw newRxError('SC15', {\n value,\n schema: jsonSchema\n });\n }\n if (value.type !== 'string') {\n throw newRxError('SC16', {\n value,\n schema: jsonSchema\n });\n }\n }\n\n // check if RxDocument-property\n if (rxDocumentProperties().includes(key)) {\n throw newRxError('SC17', {\n key,\n schema: jsonSchema\n });\n }\n });\n\n // check format of jsonSchema.indexes\n if (jsonSchema.indexes) {\n // should be an array\n if (!isMaybeReadonlyArray(jsonSchema.indexes)) {\n throw newRxError('SC18', {\n indexes: jsonSchema.indexes,\n schema: jsonSchema\n });\n }\n jsonSchema.indexes.forEach(index => {\n // should contain strings or array of strings\n if (!(typeof index === 'string' || Array.isArray(index))) {\n throw newRxError('SC19', {\n index,\n schema: jsonSchema\n });\n }\n // if is a compound index it must contain strings\n if (Array.isArray(index)) {\n for (var i = 0; i < index.length; i += 1) {\n if (typeof index[i] !== 'string') {\n throw newRxError('SC20', {\n index,\n schema: jsonSchema\n });\n }\n }\n }\n\n /**\n * To be able to craft custom indexable string with compound fields,\n * we need to know the maximum fieldlength of the fields values\n * when they are transformed to strings.\n * Therefore we need to enforce some properties inside of the schema.\n */\n var indexAsArray = isMaybeReadonlyArray(index) ? index : [index];\n indexAsArray.forEach(fieldName => {\n var schemaPart = getSchemaByObjectPath(jsonSchema, fieldName);\n var type = schemaPart.type;\n switch (type) {\n case 'string':\n var maxLength = schemaPart.maxLength;\n if (!maxLength) {\n throw newRxError('SC34', {\n index,\n field: fieldName,\n schema: jsonSchema\n });\n }\n break;\n case 'number':\n case 'integer':\n var multipleOf = schemaPart.multipleOf;\n if (!multipleOf) {\n throw newRxError('SC35', {\n index,\n field: fieldName,\n schema: jsonSchema\n });\n }\n var maximum = schemaPart.maximum;\n var minimum = schemaPart.minimum;\n if (typeof maximum === 'undefined' || typeof minimum === 'undefined') {\n throw newRxError('SC37', {\n index,\n field: fieldName,\n schema: jsonSchema\n });\n }\n if (!isFinite(maximum) || !isFinite(minimum)) {\n throw newRxError('SC41', {\n index,\n field: fieldName,\n schema: jsonSchema\n });\n }\n break;\n case 'boolean':\n /**\n * If a boolean field is used as an index,\n * it must be required.\n */\n var parentPath = '';\n var lastPathPart = fieldName;\n if (fieldName.includes('.')) {\n var partParts = fieldName.split('.');\n lastPathPart = partParts.pop();\n parentPath = partParts.join('.');\n }\n var parentSchemaPart = parentPath === '' ? jsonSchema : getSchemaByObjectPath(jsonSchema, parentPath);\n if (!parentSchemaPart.required || !parentSchemaPart.required.includes(lastPathPart)) {\n throw newRxError('SC38', {\n index,\n field: fieldName,\n schema: jsonSchema\n });\n }\n break;\n default:\n throw newRxError('SC36', {\n fieldName,\n type: schemaPart.type,\n schema: jsonSchema\n });\n }\n });\n });\n }\n\n // remove backward-compatibility for index: true\n Object.keys(flattenObject(jsonSchema)).map(key => {\n // flattenObject returns only ending paths, we need all paths pointing to an object\n var split = key.split('.');\n split.pop(); // all but last\n return split.join('.');\n }).filter(key => key !== '').filter((elem, pos, arr) => arr.indexOf(elem) === pos) // unique\n .filter(key => {\n // check if this path defines an index\n var value = getProperty(jsonSchema, key);\n return value && !!value.index;\n }).forEach(key => {\n // replace inner properties\n key = key.replace('properties.', ''); // first\n key = key.replace(/\\.properties\\./g, '.'); // middle\n throw newRxError('SC26', {\n index: trimDots(key),\n schema: jsonSchema\n });\n });\n\n /* check types of the indexes */\n (jsonSchema.indexes || []).reduce((indexPaths, currentIndex) => {\n if (isMaybeReadonlyArray(currentIndex)) {\n appendToArray(indexPaths, currentIndex);\n } else {\n indexPaths.push(currentIndex);\n }\n return indexPaths;\n }, []).filter((elem, pos, arr) => arr.indexOf(elem) === pos) // from now on working only with unique indexes\n .map(indexPath => {\n var realPath = getSchemaPropertyRealPath(indexPath); // real path in the collection schema\n var schemaObj = getProperty(jsonSchema, realPath); // get the schema of the indexed property\n if (!schemaObj || typeof schemaObj !== 'object') {\n throw newRxError('SC21', {\n index: indexPath,\n schema: jsonSchema\n });\n }\n return {\n indexPath,\n schemaObj\n };\n }).filter(index => index.schemaObj.type !== 'string' && index.schemaObj.type !== 'integer' && index.schemaObj.type !== 'number' && index.schemaObj.type !== 'boolean').forEach(index => {\n throw newRxError('SC22', {\n key: index.indexPath,\n type: index.schemaObj.type,\n schema: jsonSchema\n });\n });\n\n /* ensure encrypted fields exist in the schema */\n if (jsonSchema.encrypted) {\n jsonSchema.encrypted.forEach(propPath => {\n // real path in the collection schema\n var realPath = getSchemaPropertyRealPath(propPath);\n // get the schema of the indexed property\n var schemaObj = getProperty(jsonSchema, realPath);\n if (!schemaObj || typeof schemaObj !== 'object') {\n throw newRxError('SC28', {\n field: propPath,\n schema: jsonSchema\n });\n }\n });\n }\n}\n//# sourceMappingURL=check-schema.js.map","import { newRxError, newRxTypeError } from \"../../rx-error.js\";\nimport { rxCollectionProperties, rxDocumentProperties } from \"./entity-properties.js\";\n\n/**\n * checks if the given static methods are allowed\n * @throws if not allowed\n */\nexport function checkOrmMethods(statics) {\n if (!statics) {\n return;\n }\n Object.entries(statics).forEach(([k, v]) => {\n if (typeof k !== 'string') {\n throw newRxTypeError('COL14', {\n name: k\n });\n }\n if (k.startsWith('_')) {\n throw newRxTypeError('COL15', {\n name: k\n });\n }\n if (typeof v !== 'function') {\n throw newRxTypeError('COL16', {\n name: k,\n type: typeof k\n });\n }\n if (rxCollectionProperties().includes(k) || rxDocumentProperties().includes(k)) {\n throw newRxError('COL17', {\n name: k\n });\n }\n });\n}\nexport function checkOrmDocumentMethods(schema, methods) {\n var topLevelFields = Object.keys(schema.properties);\n if (!methods) {\n return;\n }\n Object.keys(methods).filter(funName => topLevelFields.includes(funName)).forEach(funName => {\n throw newRxError('COL18', {\n funName\n });\n });\n}\n//# sourceMappingURL=check-orm.js.map","import { newRxError, newRxTypeError } from \"../../rx-error.js\";\nimport { rxDatabaseProperties } from \"./entity-properties.js\";\nimport { isFolderPath } from \"../../plugins/utils/index.js\";\n\n/**\n * if the name of a collection\n * clashes with a property of RxDatabase,\n * we get problems so this function prohibits this\n */\nexport function ensureCollectionNameValid(args) {\n if (rxDatabaseProperties().includes(args.name)) {\n throw newRxError('DB5', {\n name: args.name\n });\n }\n validateDatabaseName(args.name);\n}\nexport function ensureDatabaseNameIsValid(args) {\n validateDatabaseName(args.name);\n if (args.name.includes('$')) {\n throw newRxError('DB13', {\n name: args.name\n });\n }\n\n /**\n * The server-plugin has problems when a path with and ending slash is given\n * So we do not allow this.\n * @link https://github.com/pubkey/rxdb/issues/2251\n */\n if (isFolderPath(args.name)) {\n if (args.name.endsWith('/') || args.name.endsWith('\\\\')) {\n throw newRxError('DB11', {\n name: args.name\n });\n }\n }\n}\nvar validCouchDBStringRegexStr = '^[a-z][_$a-z0-9\\\\-]*$';\nvar validCouchDBStringRegex = new RegExp(validCouchDBStringRegexStr);\n\n/**\n * Validates that a given string is ok to be used with couchdb-collection-names.\n * We only allow these strings as database- or collection names because it ensures\n * that you later do not get in trouble when you want to use the database together witch couchdb.\n *\n * @link https://docs.couchdb.org/en/stable/api/database/common.html\n * @link https://neighbourhood.ie/blog/2020/10/13/everything-you-need-to-know-about-couchdb-database-names/\n * @throws {RxError}\n */\nexport function validateDatabaseName(name) {\n if (typeof name !== 'string' || name.length === 0) {\n throw newRxTypeError('UT1', {\n name\n });\n }\n\n // do not check, if foldername is given\n if (isFolderPath(name)) {\n return true;\n }\n if (!name.match(validCouchDBStringRegex) &&\n /**\n * The string ':memory:' is used in the SQLite RxStorage\n * to persist data into a memory state. Often used in tests.\n */\n name !== ':memory:') {\n throw newRxError('UT2', {\n regex: validCouchDBStringRegexStr,\n givenName: name\n });\n }\n return true;\n}\n//# sourceMappingURL=unallowed-properties.js.map","import { newRxError, newRxTypeError } from \"../../rx-error.js\";\nimport { deepEqual } from \"../utils/index.js\";\nimport { prepareQuery } from \"../../rx-query-helper.js\";\n\n/**\n * accidentally passing a non-valid object into the query params\n * is very hard to debug especially when queries are observed\n * This is why we do some checks here in dev-mode\n */\nexport function checkQuery(args) {\n var isPlainObject = Object.prototype.toString.call(args.queryObj) === '[object Object]';\n if (!isPlainObject) {\n throw newRxTypeError('QU11', {\n op: args.op,\n collection: args.collection.name,\n queryObj: args.queryObj\n });\n }\n var validKeys = ['selector', 'limit', 'skip', 'sort', 'index'];\n Object.keys(args.queryObj).forEach(key => {\n if (!validKeys.includes(key)) {\n throw newRxTypeError('QU11', {\n op: args.op,\n collection: args.collection.name,\n queryObj: args.queryObj,\n key,\n args: {\n validKeys\n }\n });\n }\n });\n\n // do not allow skip or limit for count queries\n if (args.op === 'count' && (args.queryObj.limit || args.queryObj.skip)) {\n throw newRxError('QU15', {\n collection: args.collection.name,\n query: args.queryObj\n });\n }\n ensureObjectDoesNotContainRegExp(args.queryObj);\n}\nexport function checkMangoQuery(args) {\n var schema = args.rxQuery.collection.schema.jsonSchema;\n\n /**\n * Ensure that all top level fields are included in the schema.\n * TODO this check can be augmented to also check sub-fields.\n */\n var massagedSelector = args.mangoQuery.selector;\n var schemaTopLevelFields = Object.keys(schema.properties);\n Object.keys(massagedSelector)\n // do not check operators\n .filter(fieldOrOperator => !fieldOrOperator.startsWith('$'))\n // skip this check on non-top-level fields\n .filter(field => !field.includes('.')).forEach(field => {\n if (!schemaTopLevelFields.includes(field)) {\n throw newRxError('QU13', {\n schema,\n field,\n query: args.mangoQuery\n });\n }\n });\n\n /**\n * ensure if custom index is set,\n * it is also defined in the schema\n */\n var schemaIndexes = schema.indexes ? schema.indexes : [];\n var index = args.mangoQuery.index;\n if (index) {\n var isInSchema = schemaIndexes.find(schemaIndex => deepEqual(schemaIndex, index));\n if (!isInSchema) {\n throw newRxError('QU12', {\n collection: args.rxQuery.collection.name,\n query: args.mangoQuery,\n schema\n });\n }\n }\n\n /**\n * Ensure that a count() query can only be used\n * with selectors that are fully satisfied by the used index.\n */\n if (args.rxQuery.op === 'count') {\n if (!areSelectorsSatisfiedByIndex(args.rxQuery.collection.schema.jsonSchema, args.mangoQuery) && !args.rxQuery.collection.database.allowSlowCount) {\n throw newRxError('QU14', {\n collection: args.rxQuery.collection,\n query: args.mangoQuery\n });\n }\n }\n\n /**\n * Ensure that sort only runs on known fields\n * TODO also check nested fields\n */\n if (args.mangoQuery.sort) {\n args.mangoQuery.sort.map(sortPart => Object.keys(sortPart)[0]).filter(field => !field.includes('.')).forEach(field => {\n if (!schemaTopLevelFields.includes(field)) {\n throw newRxError('QU13', {\n schema,\n field,\n query: args.mangoQuery\n });\n }\n });\n }\n\n // Do not allow RexExp instances\n ensureObjectDoesNotContainRegExp(args.mangoQuery);\n}\nexport function areSelectorsSatisfiedByIndex(schema, query) {\n var preparedQuery = prepareQuery(schema, query);\n return preparedQuery.queryPlan.selectorSatisfiedByIndex;\n}\n\n/**\n * Ensures that the selector does not contain any RegExp instance.\n * @recursive\n */\nexport function ensureObjectDoesNotContainRegExp(selector) {\n if (typeof selector !== 'object' || selector === null) {\n return;\n }\n var keys = Object.keys(selector);\n keys.forEach(key => {\n var value = selector[key];\n if (value instanceof RegExp) {\n throw newRxError('QU16', {\n field: key,\n query: selector\n });\n } else if (Array.isArray(value)) {\n value.forEach(item => ensureObjectDoesNotContainRegExp(item));\n } else {\n ensureObjectDoesNotContainRegExp(value);\n }\n });\n}\n\n/**\n * People often use queries wrong\n * so we have some checks here.\n * For example people use numbers as primary keys\n * which is not allowed.\n */\nexport function isQueryAllowed(args) {\n if (args.op === 'findOne') {\n if (typeof args.queryObj === 'number' || Array.isArray(args.queryObj)) {\n throw newRxTypeError('COL6', {\n collection: args.collection.name,\n queryObj: args.queryObj\n });\n }\n } else if (args.op === 'find') {\n if (typeof args.queryObj === 'string') {\n throw newRxError('COL5', {\n collection: args.collection.name,\n queryObj: args.queryObj\n });\n }\n }\n}\n//# sourceMappingURL=check-query.js.map","import { newRxError } from \"../../rx-error.js\";\nimport { fillPrimaryKey, getPrimaryFieldOfPrimaryKey } from \"../../rx-schema-helper.js\";\nexport function ensurePrimaryKeyValid(primaryKey, docData) {\n if (!primaryKey) {\n throw newRxError('DOC20', {\n primaryKey,\n document: docData\n });\n }\n\n /**\n * This is required so that we can left-pad\n * the primaryKey and we are still able to de-left-pad\n * it to get again the original key.\n */\n if (primaryKey !== primaryKey.trim()) {\n throw newRxError('DOC21', {\n primaryKey,\n document: docData\n });\n }\n if (primaryKey.includes('\\r') || primaryKey.includes('\\n')) {\n throw newRxError('DOC22', {\n primaryKey,\n document: docData\n });\n }\n if (primaryKey.includes('\"')) {\n throw newRxError('DOC23', {\n primaryKey,\n document: docData\n });\n }\n}\n\n/**\n * Deeply checks if the object contains an\n * instance of the JavaScript Date class.\n * @recursive\n */\nexport function containsDateInstance(obj) {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n for (var key in obj) {\n if (obj.hasOwnProperty(key)) {\n if (obj[key] instanceof Date) {\n return true;\n }\n if (typeof obj[key] === 'object' && containsDateInstance(obj[key])) {\n return true;\n }\n }\n }\n return false;\n}\nexport function checkWriteRows(storageInstance, rows) {\n var primaryPath = getPrimaryFieldOfPrimaryKey(storageInstance.schema.primaryKey);\n var _loop = function (writeRow) {\n // ensure that the primary key has not been changed\n writeRow.document = fillPrimaryKey(primaryPath, storageInstance.schema, writeRow.document);\n\n /**\n * Ensure that _meta fields have been merged\n * and not replaced.\n * This is important so that when one plugin A\n * sets a _meta field and another plugin B does a write\n * to the document, it must be ensured that the\n * field of plugin A was not removed.\n */\n if (writeRow.previous) {\n Object.keys(writeRow.previous._meta).forEach(metaFieldName => {\n if (!Object.prototype.hasOwnProperty.call(writeRow.document._meta, metaFieldName)) {\n throw newRxError('SNH', {\n dataBefore: writeRow.previous,\n dataAfter: writeRow.document,\n args: {\n metaFieldName\n }\n });\n }\n });\n }\n\n /**\n * Ensure it can be structured cloned\n */\n try {\n /**\n * Notice that structuredClone() is not available\n * in ReactNative, so we test for JSON.stringify() instead\n * @link https://github.com/pubkey/rxdb/issues/5046#issuecomment-1827374498\n */\n if (typeof structuredClone === 'function') {\n structuredClone(writeRow);\n } else {\n JSON.parse(JSON.stringify(writeRow));\n }\n } catch (err) {\n throw newRxError('DOC24', {\n collection: storageInstance.collectionName,\n document: writeRow.document\n });\n }\n\n /**\n * Ensure it does not contain a Date() object\n */\n if (containsDateInstance(writeRow.document)) {\n throw newRxError('DOC24', {\n collection: storageInstance.collectionName,\n document: writeRow.document\n });\n }\n };\n for (var writeRow of rows) {\n _loop(writeRow);\n }\n}\n//# sourceMappingURL=check-document.js.map","import { RXDB_VERSION, hasPremiumFlag } from \"../utils/index.js\";\nvar iframeShown = false;\n\n/**\n * Adds an iframe to track the results of marketing efforts.\n */\nexport async function addDevModeTrackingIframe(db) {\n /**\n * Only run this in browser AND localhost AND dev-mode.\n * Make sure this is never used in production by someone.\n */\n if (iframeShown || typeof window === 'undefined' || typeof location === 'undefined'\n // !isLocalHost()\n ) {\n return;\n }\n\n // do not show if premium flag is set.\n if (await hasPremiumFlag()) {\n return;\n }\n iframeShown = true;\n var iframe = document.createElement('iframe');\n /**\n * Do not use display:none\n * @link https://medium.com/@zachcaceres/dont-use-display-none-to-hide-iframes-in-safari-b51715eb22c4\n */\n iframe.style.visibility = 'hidden';\n iframe.width = '1px';\n iframe.height = '1px';\n iframe.style.position = 'absolute';\n iframe.style.top = '0';\n iframe.style.left = '0';\n iframe.style.opacity = '0.1';\n iframe.src = 'https://rxdb.info/html/dev-mode-iframe.html?version=' + RXDB_VERSION;\n document.body.appendChild(iframe);\n}\nfunction isLocalHost() {\n return location.hostname === 'localhost' || location.hostname.includes('localhost') || location.hostname === '127.0.0.1' || location.hostname === '0.0.0.0' || location.hostname === '[::1]' // IPv6\n ;\n}\n//# sourceMappingURL=dev-mode-tracking.js.map","import { ERROR_MESSAGES } from \"./error-messages.js\";\nimport { checkSchema } from \"./check-schema.js\";\nimport { checkOrmDocumentMethods, checkOrmMethods } from \"./check-orm.js\";\nimport { checkMigrationStrategies } from \"./check-migration-strategies.js\";\nimport { ensureCollectionNameValid, ensureDatabaseNameIsValid } from \"./unallowed-properties.js\";\nimport { checkMangoQuery, checkQuery, isQueryAllowed } from \"./check-query.js\";\nimport { newRxError } from \"../../rx-error.js\";\nimport { deepFreeze } from \"../../plugins/utils/index.js\";\nimport { checkWriteRows, ensurePrimaryKeyValid } from \"./check-document.js\";\nimport { addDevModeTrackingIframe } from \"./dev-mode-tracking.js\";\nexport * from \"./check-schema.js\";\nexport * from \"./unallowed-properties.js\";\nexport * from \"./check-query.js\";\nvar showDevModeWarning = true;\n\n/**\n * Suppresses the warning message shown in the console, typically invoked once the developer (hello!) \n * has acknowledged it.\n */\nexport function disableWarnings() {\n showDevModeWarning = false;\n}\n\n/**\n * Deep freezes and object when in dev-mode.\n * Deep-Freezing has the same performance as deep-cloning, so we only do that in dev-mode.\n * Also we can ensure the readonly state via typescript\n * @link https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze\n */\nexport function deepFreezeWhenDevMode(obj) {\n // direct return if not suitable for deepFreeze()\n if (!obj || typeof obj === 'string' || typeof obj === 'number') {\n return obj;\n }\n return deepFreeze(obj);\n}\nexport var DEV_MODE_PLUGIN_NAME = 'dev-mode';\nexport var RxDBDevModePlugin = {\n name: DEV_MODE_PLUGIN_NAME,\n rxdb: true,\n init: () => {\n if (showDevModeWarning) {\n console.warn(['-------------- RxDB dev-mode warning -------------------------------', 'you are seeing this because you use the RxDB dev-mode plugin https://rxdb.info/dev-mode.html?console=dev-mode ', 'This is great in development mode, because it will run many checks to ensure', 'that you use RxDB correct. If you see this in production mode,', 'you did something wrong because the dev-mode plugin will decrease the performance.', '', '🤗 Hint: To get the most out of RxDB, check out the Premium Plugins', 'to get access to faster storages and more professional features: https://rxdb.info/premium?console=dev-mode ', '', 'You can disable this warning by calling disableWarnings() from the dev-mode plugin.',\n // '',\n // 'Also take part in the RxDB User Survey: https://rxdb.info/survey.html',\n '---------------------------------------------------------------------'].join('\\n'));\n }\n },\n overwritable: {\n isDevMode() {\n return true;\n },\n deepFreezeWhenDevMode,\n tunnelErrorMessage(code) {\n if (!ERROR_MESSAGES[code]) {\n console.error('RxDB: Error-Code not known: ' + code);\n throw new Error('Error-Code ' + code + ' not known, contact the maintainer');\n }\n return ERROR_MESSAGES[code];\n }\n },\n hooks: {\n preCreateRxSchema: {\n after: checkSchema\n },\n preCreateRxDatabase: {\n before: function (args) {\n if (!args.storage.name.startsWith('validate-')) {\n throw newRxError('DVM1', {\n database: args.name,\n storage: args.storage.name\n });\n }\n },\n after: function (args) {\n ensureDatabaseNameIsValid(args);\n }\n },\n createRxDatabase: {\n after: async function (args) {\n addDevModeTrackingIframe(args.database);\n }\n },\n preCreateRxCollection: {\n after: function (args) {\n ensureCollectionNameValid(args);\n checkOrmDocumentMethods(args.schema, args.methods);\n if (args.name.charAt(0) === '_') {\n throw newRxError('DB2', {\n name: args.name\n });\n }\n if (!args.schema) {\n throw newRxError('DB4', {\n name: args.name,\n args\n });\n }\n }\n },\n createRxDocument: {\n before: function (doc) {\n ensurePrimaryKeyValid(doc.primary, doc.toJSON(true));\n }\n },\n prePrepareRxQuery: {\n after: function (args) {\n isQueryAllowed(args);\n }\n },\n preCreateRxQuery: {\n after: function (args) {\n checkQuery(args);\n }\n },\n prePrepareQuery: {\n after: args => {\n checkMangoQuery(args);\n }\n },\n preStorageWrite: {\n before: args => {\n checkWriteRows(args.storageInstance, args.rows);\n }\n },\n createRxCollection: {\n after: args => {\n // check ORM-methods\n checkOrmMethods(args.creator.statics);\n checkOrmMethods(args.creator.methods);\n checkOrmMethods(args.creator.attachments);\n\n // check migration strategies\n if (args.creator.schema && args.creator.migrationStrategies) {\n checkMigrationStrategies(args.creator.schema, args.creator.migrationStrategies);\n }\n }\n }\n }\n};\n//# sourceMappingURL=index.js.map","import { newRxTypeError, newRxError } from \"../../rx-error.js\";\nimport { getPreviousVersions } from \"../../rx-schema.js\";\n\n/**\n * checks if the migrationStrategies are ok, throws if not\n * @throws {Error|TypeError} if not ok\n */\nexport function checkMigrationStrategies(schema, migrationStrategies) {\n // migrationStrategies must be object not array\n if (typeof migrationStrategies !== 'object' || Array.isArray(migrationStrategies)) {\n throw newRxTypeError('COL11', {\n schema\n });\n }\n var previousVersions = getPreviousVersions(schema);\n\n // for every previousVersion there must be strategy\n if (previousVersions.length !== Object.keys(migrationStrategies).length) {\n throw newRxError('COL12', {\n have: Object.keys(migrationStrategies),\n should: previousVersions\n });\n }\n\n // every strategy must have number as property and be a function\n previousVersions.map(vNr => ({\n v: vNr,\n s: migrationStrategies[vNr + 1]\n })).filter(strategy => typeof strategy.s !== 'function').forEach(strategy => {\n throw newRxTypeError('COL13', {\n version: strategy.v,\n type: typeof strategy,\n schema\n });\n });\n return true;\n}\n//# sourceMappingURL=check-migration-strategies.js.map"],"names":["_rxCollectionProperties","_rxDatabaseProperties","ERROR_MESSAGES","UT1","UT2","UT3","UT4","UT5","UT6","UT7","PL1","PL3","P2","QU1","QU4","QU5","QU6","QU9","QU10","QU11","QU12","QU13","QU14","QU15","QU16","QU17","QU18","MQ1","MQ2","MQ3","MQ4","MQ5","MQ6","MQ7","MQ8","DB1","DB2","DB3","DB4","DB5","DB6","DB8","DB9","DB11","DB12","DB13","DB14","COL1","COL2","COL3","COL4","COL5","COL6","COL7","COL8","COL9","COL10","COL11","COL12","COL13","COL14","COL15","COL16","COL17","COL18","COL20","COL21","CONFLICT","COL22","COL23","DOC1","DOC2","DOC3","DOC4","DOC5","DOC6","DOC7","DOC8","DOC9","DOC10","DOC11","DOC13","DOC14","DOC15","DOC16","DOC17","DOC18","DOC19","DOC20","DOC21","DOC22","DOC23","DOC24","DM1","DM2","DM3","DM4","DM5","AT1","EN1","EN2","EN3","EN4","JD1","JD2","JD3","LD1","LD2","LD3","LD4","LD5","LD6","LD7","LD8","RC1","RC2","RC4","RC5","RC6","RC7","RC_PULL","RC_STREAM","RC_PUSH","RC_PUSH_NO_AR","RC_WEBRTC_PEER","RC_COUCHDB_1","RC_COUCHDB_2","RC_OUTDATED","RC_UNAUTHORIZED","RC_FORBIDDEN","SC1","SC2","SC3","SC4","SC6","SC7","SC8","SC10","SC11","SC13","SC14","SC15","SC16","SC17","SC18","SC19","SC20","SC21","SC22","SC23","SC24","SC25","SC26","SC28","SC29","SC30","SC32","SC33","SC34","SC35","SC36","SC37","SC38","SC39","SC40","SC41","DVM1","VD1","VD2","S1","GQL1","GQL3","CRDT1","CRDT2","CRDT3","DXE1","RM1","SNH","_rxDocumentProperties","pseudoRxDocument","rxDocumentProperties","ownProperties","Object","getOwnPropertyNames","prototypeProperties","checkFieldNameRegex","fieldName","includes","regexStr","regex","RegExp","match","validateFieldsDeep","rxJsonSchema","primaryPath","primaryKey","traverse","currentObj","currentPath","keys","forEach","attributeName","schemaObj","properties","Array","isArray","path","split","pop","prototype","hasOwnProperty","call","type","required","length","items","isNested","default","charAt","checkField","nextPath","checkPrimaryKey","jsonSchema","schema","validatePrimarySchemaPart","schemaPart","args","key","compositePrimaryKey","fields","field","primaryPathSchemaPart","maxLength","isFinite","getSchemaPropertyRealPath","shortPath","pathParts","realPath","i","concat","checkSchema","_rev","version","value","indexes","unique","encrypted","index","multipleOf","maximum","minimum","parentPath","lastPathPart","partParts","join","parentSchemaPart","map","filter","elem","pos","arr","indexOf","replace","reduce","indexPaths","currentIndex","push","indexPath","propPath","checkOrmMethods","statics","entries","k","v","name","startsWith","pseudoInstance","getPrototypeOf","rxCollectionProperties","ensureCollectionNameValid","close","rxDatabaseProperties","validateDatabaseName","ensureDatabaseNameIsValid","endsWith","validCouchDBStringRegexStr","validCouchDBStringRegex","givenName","checkQuery","toString","queryObj","op","collection","validKeys","limit","skip","query","ensureObjectDoesNotContainRegExp","checkMangoQuery","rxQuery","massagedSelector","mangoQuery","selector","schemaTopLevelFields","fieldOrOperator","schemaIndexes","find","schemaIndex","areSelectorsSatisfiedByIndex","database","allowSlowCount","sort","sortPart","queryPlan","selectorSatisfiedByIndex","item","isQueryAllowed","containsDateInstance","obj","Date","checkWriteRows","storageInstance","rows","_loop","writeRow","document","previous","_meta","metaFieldName","dataBefore","dataAfter","structuredClone","JSON","parse","stringify","err","collectionName","iframeShown","showDevModeWarning","disableWarnings","deepFreezeWhenDevMode","DEV_MODE_PLUGIN_NAME","RxDBDevModePlugin","rxdb","init","console","warn","overwritable","isDevMode","tunnelErrorMessage","code","error","Error","hooks","preCreateRxSchema","after","preCreateRxDatabase","before","storage","createRxDatabase","async","window","location","iframe","createElement","style","visibility","width","height","position","top","left","opacity","src","body","appendChild","addDevModeTrackingIframe","preCreateRxCollection","methods","topLevelFields","funName","createRxDocument","doc","docData","trim","ensurePrimaryKeyValid","primary","toJSON","prePrepareRxQuery","preCreateRxQuery","prePrepareQuery","preStorageWrite","createRxCollection","creator","attachments","migrationStrategies","previousVersions","have","should","vNr","s","strategy","checkMigrationStrategies"],"sourceRoot":""} -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | RxDB Quickstart • TodoMVCFork me on GitHub

p2p todos

This is a local first todo app that stores data locally with RxDB and replicates it peer-to-peer with WebRTC to other devices without sending the data through any central server. Open this url in another browser/device/tab to test the replication:

The whole app is implemented without a framework in about 200 lines of TypeScript code. To learn more about how it works, I recommend looking at the source code and read the RxDB Quickstart Guide.


      -------------------------------------------------------------------------------- /docs/src/style.css: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | html, 4 | body { 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | button { 10 | margin: 0; 11 | padding: 0; 12 | border: 0; 13 | background: none; 14 | font-size: 100%; 15 | vertical-align: baseline; 16 | font-family: inherit; 17 | font-weight: inherit; 18 | color: inherit; 19 | -webkit-appearance: none; 20 | appearance: none; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale; 23 | } 24 | 25 | body { 26 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 27 | line-height: 1.4em; 28 | background: #f5f5f5; 29 | color: #111111; 30 | min-width: 230px; 31 | max-width: 550px; 32 | margin: 0 auto; 33 | -webkit-font-smoothing: antialiased; 34 | -moz-osx-font-smoothing: grayscale; 35 | font-weight: 300; 36 | } 37 | 38 | .hidden { 39 | display: none; 40 | } 41 | 42 | .todoapp { 43 | background: #fff; 44 | margin: 130px 0 40px 0; 45 | position: relative; 46 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 47 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 48 | } 49 | 50 | .todoapp input::-webkit-input-placeholder { 51 | font-style: italic; 52 | font-weight: 400; 53 | color: rgba(0, 0, 0, 0.4); 54 | } 55 | 56 | .todoapp input::-moz-placeholder { 57 | font-style: italic; 58 | font-weight: 400; 59 | color: rgba(0, 0, 0, 0.4); 60 | } 61 | 62 | .todoapp input::input-placeholder { 63 | font-style: italic; 64 | font-weight: 400; 65 | color: rgba(0, 0, 0, 0.4); 66 | } 67 | 68 | .todoapp h1 { 69 | position: absolute; 70 | top: -140px; 71 | width: 100%; 72 | font-size: 80px; 73 | font-weight: 200; 74 | text-align: center; 75 | color: #b83f45; 76 | -webkit-text-rendering: optimizeLegibility; 77 | -moz-text-rendering: optimizeLegibility; 78 | text-rendering: optimizeLegibility; 79 | } 80 | 81 | .new-todo, 82 | .edit { 83 | position: relative; 84 | margin: 0; 85 | width: 100%; 86 | font-size: 24px; 87 | font-family: inherit; 88 | font-weight: inherit; 89 | line-height: 1.4em; 90 | color: inherit; 91 | padding: 6px; 92 | border: 1px solid #999; 93 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 94 | box-sizing: border-box; 95 | -webkit-font-smoothing: antialiased; 96 | -moz-osx-font-smoothing: grayscale; 97 | } 98 | 99 | .new-todo { 100 | padding: 16px 16px 16px 60px; 101 | height: 65px; 102 | border: none; 103 | background: rgba(0, 0, 0, 0.003); 104 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 105 | } 106 | 107 | .main { 108 | position: relative; 109 | z-index: 2; 110 | border-top: 1px solid #e6e6e6; 111 | } 112 | 113 | .toggle-all { 114 | width: 1px; 115 | height: 1px; 116 | border: none; /* Mobile Safari */ 117 | opacity: 0; 118 | position: absolute; 119 | right: 100%; 120 | bottom: 100%; 121 | } 122 | 123 | .toggle-all + label { 124 | display: flex; 125 | align-items: center; 126 | justify-content: center; 127 | width: 45px; 128 | height: 65px; 129 | font-size: 0; 130 | position: absolute; 131 | top: -65px; 132 | left: -0; 133 | } 134 | 135 | .toggle-all + label:before { 136 | content: '❯'; 137 | display: inline-block; 138 | font-size: 22px; 139 | color: #949494; 140 | padding: 10px 27px 10px 27px; 141 | -webkit-transform: rotate(90deg); 142 | transform: rotate(90deg); 143 | } 144 | 145 | .toggle-all:checked + label:before { 146 | color: #484848; 147 | } 148 | 149 | .todo-list { 150 | margin: 0; 151 | padding: 0; 152 | list-style: none; 153 | } 154 | 155 | .todo-list li { 156 | position: relative; 157 | font-size: 24px; 158 | border-bottom: 1px solid #ededed; 159 | } 160 | 161 | .todo-list li:last-child { 162 | border-bottom: none; 163 | } 164 | 165 | .todo-list li.editing { 166 | border-bottom: none; 167 | padding: 0; 168 | } 169 | 170 | .todo-list li.editing .edit { 171 | display: block; 172 | width: calc(100% - 43px); 173 | padding: 12px 16px; 174 | margin: 0 0 0 43px; 175 | } 176 | 177 | .todo-list li.editing .view { 178 | display: none; 179 | } 180 | 181 | .todo-list li .toggle { 182 | text-align: center; 183 | width: 40px; 184 | /* auto, since non-WebKit browsers doesn't support input styling */ 185 | height: auto; 186 | position: absolute; 187 | top: 0; 188 | bottom: 0; 189 | margin: auto 0; 190 | border: none; /* Mobile Safari */ 191 | -webkit-appearance: none; 192 | appearance: none; 193 | } 194 | 195 | .todo-list li .toggle { 196 | opacity: 0; 197 | } 198 | 199 | .todo-list li .toggle + label { 200 | /* 201 | Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 202 | IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ 203 | */ 204 | background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E"); 205 | background-repeat: no-repeat; 206 | background-position: center left; 207 | } 208 | 209 | .todo-list li .toggle:checked + label { 210 | background-image: url("data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E"); 211 | } 212 | 213 | .todo-list li label { 214 | overflow-wrap: break-word; 215 | padding: 15px 15px 15px 60px; 216 | display: block; 217 | line-height: 1.2; 218 | transition: color 0.4s; 219 | font-weight: 400; 220 | color: #484848; 221 | } 222 | 223 | .todo-list li.completed label { 224 | color: #949494; 225 | text-decoration: line-through; 226 | } 227 | 228 | .todo-list li .destroy { 229 | display: none; 230 | position: absolute; 231 | top: 0; 232 | right: 10px; 233 | bottom: 0; 234 | width: 40px; 235 | height: 40px; 236 | margin: auto 0; 237 | font-size: 30px; 238 | color: #949494; 239 | transition: color 0.2s ease-out; 240 | } 241 | 242 | .todo-list li .destroy:hover, 243 | .todo-list li .destroy:focus { 244 | color: #C18585; 245 | } 246 | 247 | .todo-list li .destroy:after { 248 | content: '×'; 249 | display: block; 250 | height: 100%; 251 | line-height: 1.1; 252 | } 253 | 254 | .todo-list li:hover .destroy { 255 | display: block; 256 | } 257 | 258 | .todo-list li .edit { 259 | display: none; 260 | } 261 | 262 | .todo-list li.editing:last-child { 263 | margin-bottom: -1px; 264 | } 265 | 266 | .footer { 267 | padding: 10px 15px; 268 | height: 20px; 269 | text-align: center; 270 | font-size: 15px; 271 | border-top: 1px solid #e6e6e6; 272 | } 273 | 274 | .footer:before { 275 | content: ''; 276 | position: absolute; 277 | right: 0; 278 | bottom: 0; 279 | left: 0; 280 | height: 50px; 281 | overflow: hidden; 282 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 283 | 0 8px 0 -3px #f6f6f6, 284 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 285 | 0 16px 0 -6px #f6f6f6, 286 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 287 | } 288 | 289 | .todo-count { 290 | float: left; 291 | text-align: left; 292 | } 293 | 294 | .todo-count strong { 295 | font-weight: 300; 296 | } 297 | 298 | .filters { 299 | margin: 0; 300 | padding: 0; 301 | list-style: none; 302 | position: absolute; 303 | right: 0; 304 | left: 0; 305 | } 306 | 307 | .filters li { 308 | display: inline; 309 | } 310 | 311 | .filters li a { 312 | color: inherit; 313 | margin: 3px; 314 | padding: 3px 7px; 315 | text-decoration: none; 316 | border: 1px solid transparent; 317 | border-radius: 3px; 318 | } 319 | 320 | .filters li a:hover { 321 | border-color: #DB7676; 322 | } 323 | 324 | .filters li a.selected { 325 | border-color: #CE4646; 326 | } 327 | 328 | .clear-completed, 329 | html .clear-completed:active { 330 | float: right; 331 | position: relative; 332 | line-height: 19px; 333 | text-decoration: none; 334 | cursor: pointer; 335 | } 336 | 337 | .clear-completed:hover { 338 | text-decoration: underline; 339 | } 340 | 341 | .info { 342 | margin: 65px auto 0; 343 | color: #4d4d4d; 344 | font-size: 11px; 345 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 346 | text-align: center; 347 | } 348 | 349 | .info p { 350 | line-height: 1; 351 | } 352 | 353 | .info a { 354 | color: inherit; 355 | text-decoration: none; 356 | font-weight: 400; 357 | } 358 | 359 | .info a:hover { 360 | text-decoration: underline; 361 | } 362 | 363 | /* 364 | Hack to remove background from Mobile Safari. 365 | Can't use it globally since it destroys checkboxes in Firefox 366 | */ 367 | @media screen and (-webkit-min-device-pixel-ratio:0) { 368 | .toggle-all, 369 | .todo-list li .toggle { 370 | background: none; 371 | } 372 | 373 | .todo-list li .toggle { 374 | height: 40px; 375 | } 376 | } 377 | 378 | @media (max-width: 430px) { 379 | .footer { 380 | height: 50px; 381 | } 382 | 383 | .filters { 384 | bottom: 10px; 385 | } 386 | } 387 | 388 | :focus, 389 | .toggle:focus + label, 390 | .toggle-all:focus + label { 391 | box-shadow: 0 0 2px 2px #CF7D7D; 392 | outline: 0; 393 | } 394 | 395 | .description { 396 | padding: 15px; 397 | margin-bottom: 10px; 398 | ; 399 | } 400 | 401 | code { 402 | display: block; 403 | padding: 15px; 404 | 405 | 406 | /** 407 | * https://css-tricks.com/better-line-breaks-for-long-urls/ 408 | */ 409 | overflow-wrap: break-word; 410 | word-wrap: break-word; 411 | word-break: break-word; 412 | hyphens: auto; 413 | } 414 | 415 | a { 416 | color: #b83f45; 417 | font-weight: bold; 418 | text-decoration: none; 419 | } 420 | 421 | a:visited { 422 | color: #b83f45; 423 | } 424 | 425 | 426 | /** 427 | * @link https://codepo8.github.io/css-fork-on-github-ribbon/ 428 | */ 429 | #forkongithub a { 430 | background: #000; 431 | color: #fff; 432 | text-decoration: none; 433 | font-family: arial, sans-serif; 434 | text-align: center; 435 | font-weight: bold; 436 | padding: 5px 40px; 437 | font-size: 1rem; 438 | line-height: 2rem; 439 | position: relative; 440 | transition: 0.5s; 441 | } 442 | 443 | #forkongithub a:hover { 444 | background: #b83f45; 445 | color: #fff; 446 | } 447 | 448 | #forkongithub a::before, 449 | #forkongithub a::after { 450 | content: ""; 451 | width: 100%; 452 | display: block; 453 | position: absolute; 454 | top: 1px; 455 | left: 0; 456 | height: 1px; 457 | background: #fff; 458 | } 459 | 460 | #forkongithub a::after { 461 | bottom: 1px; 462 | top: auto; 463 | } 464 | 465 | @media screen and (min-width:800px) { 466 | #forkongithub { 467 | position: fixed; 468 | display: block; 469 | top: 0; 470 | right: 0; 471 | width: 200px; 472 | overflow: hidden; 473 | height: 200px; 474 | z-index: 9999; 475 | } 476 | 477 | #forkongithub a { 478 | width: 200px; 479 | position: absolute; 480 | top: 60px; 481 | right: -60px; 482 | transform: rotate(45deg); 483 | -webkit-transform: rotate(45deg); 484 | -ms-transform: rotate(45deg); 485 | -moz-transform: rotate(45deg); 486 | -o-transform: rotate(45deg); 487 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8); 488 | } 489 | } 490 | 491 | 492 | /*# sourceMappingURL=style.css.map*/ -------------------------------------------------------------------------------- /docs/src/style.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"src/style.css","mappings":"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","sources":["webpack://rxdb-quickstart/./node_modules/todomvc-app-css/index.css","webpack://rxdb-quickstart/./src/style.css"],"sourcesContent":["@charset 'utf-8';\n\nhtml,\nbody {\n\tmargin: 0;\n\tpadding: 0;\n}\n\nbutton {\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: none;\n\tfont-size: 100%;\n\tvertical-align: baseline;\n\tfont-family: inherit;\n\tfont-weight: inherit;\n\tcolor: inherit;\n\t-webkit-appearance: none;\n\tappearance: none;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n}\n\nbody {\n\tfont: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;\n\tline-height: 1.4em;\n\tbackground: #f5f5f5;\n\tcolor: #111111;\n\tmin-width: 230px;\n\tmax-width: 550px;\n\tmargin: 0 auto;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n\tfont-weight: 300;\n}\n\n.hidden {\n\tdisplay: none;\n}\n\n.todoapp {\n\tbackground: #fff;\n\tmargin: 130px 0 40px 0;\n\tposition: relative;\n\tbox-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),\n\t 0 25px 50px 0 rgba(0, 0, 0, 0.1);\n}\n\n.todoapp input::-webkit-input-placeholder {\n\tfont-style: italic;\n\tfont-weight: 400;\n\tcolor: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp input::-moz-placeholder {\n\tfont-style: italic;\n\tfont-weight: 400;\n\tcolor: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp input::input-placeholder {\n\tfont-style: italic;\n\tfont-weight: 400;\n\tcolor: rgba(0, 0, 0, 0.4);\n}\n\n.todoapp h1 {\n\tposition: absolute;\n\ttop: -140px;\n\twidth: 100%;\n\tfont-size: 80px;\n\tfont-weight: 200;\n\ttext-align: center;\n\tcolor: #b83f45;\n\t-webkit-text-rendering: optimizeLegibility;\n\t-moz-text-rendering: optimizeLegibility;\n\ttext-rendering: optimizeLegibility;\n}\n\n.new-todo,\n.edit {\n\tposition: relative;\n\tmargin: 0;\n\twidth: 100%;\n\tfont-size: 24px;\n\tfont-family: inherit;\n\tfont-weight: inherit;\n\tline-height: 1.4em;\n\tcolor: inherit;\n\tpadding: 6px;\n\tborder: 1px solid #999;\n\tbox-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);\n\tbox-sizing: border-box;\n\t-webkit-font-smoothing: antialiased;\n\t-moz-osx-font-smoothing: grayscale;\n}\n\n.new-todo {\n\tpadding: 16px 16px 16px 60px;\n\theight: 65px;\n\tborder: none;\n\tbackground: rgba(0, 0, 0, 0.003);\n\tbox-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);\n}\n\n.main {\n\tposition: relative;\n\tz-index: 2;\n\tborder-top: 1px solid #e6e6e6;\n}\n\n.toggle-all {\n\twidth: 1px;\n\theight: 1px;\n\tborder: none; /* Mobile Safari */\n\topacity: 0;\n\tposition: absolute;\n\tright: 100%;\n\tbottom: 100%;\n}\n\n.toggle-all + label {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 45px;\n\theight: 65px;\n\tfont-size: 0;\n\tposition: absolute;\n\ttop: -65px;\n\tleft: -0;\n}\n\n.toggle-all + label:before {\n\tcontent: '❯';\n\tdisplay: inline-block;\n\tfont-size: 22px;\n\tcolor: #949494;\n\tpadding: 10px 27px 10px 27px;\n\t-webkit-transform: rotate(90deg);\n\ttransform: rotate(90deg);\n}\n\n.toggle-all:checked + label:before {\n\tcolor: #484848;\n}\n\n.todo-list {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.todo-list li {\n\tposition: relative;\n\tfont-size: 24px;\n\tborder-bottom: 1px solid #ededed;\n}\n\n.todo-list li:last-child {\n\tborder-bottom: none;\n}\n\n.todo-list li.editing {\n\tborder-bottom: none;\n\tpadding: 0;\n}\n\n.todo-list li.editing .edit {\n\tdisplay: block;\n\twidth: calc(100% - 43px);\n\tpadding: 12px 16px;\n\tmargin: 0 0 0 43px;\n}\n\n.todo-list li.editing .view {\n\tdisplay: none;\n}\n\n.todo-list li .toggle {\n\ttext-align: center;\n\twidth: 40px;\n\t/* auto, since non-WebKit browsers doesn't support input styling */\n\theight: auto;\n\tposition: absolute;\n\ttop: 0;\n\tbottom: 0;\n\tmargin: auto 0;\n\tborder: none; /* Mobile Safari */\n\t-webkit-appearance: none;\n\tappearance: none;\n}\n\n.todo-list li .toggle {\n\topacity: 0;\n}\n\n.todo-list li .toggle + label {\n\t/*\n\t\tFirefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433\n\t\tIE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/\n\t*/\n\tbackground-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23949494%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');\n\tbackground-repeat: no-repeat;\n\tbackground-position: center left;\n}\n\n.todo-list li .toggle:checked + label {\n\tbackground-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E');\n}\n\n.todo-list li label {\n\toverflow-wrap: break-word;\n\tpadding: 15px 15px 15px 60px;\n\tdisplay: block;\n\tline-height: 1.2;\n\ttransition: color 0.4s;\n\tfont-weight: 400;\n\tcolor: #484848;\n}\n\n.todo-list li.completed label {\n\tcolor: #949494;\n\ttext-decoration: line-through;\n}\n\n.todo-list li .destroy {\n\tdisplay: none;\n\tposition: absolute;\n\ttop: 0;\n\tright: 10px;\n\tbottom: 0;\n\twidth: 40px;\n\theight: 40px;\n\tmargin: auto 0;\n\tfont-size: 30px;\n\tcolor: #949494;\n\ttransition: color 0.2s ease-out;\n}\n\n.todo-list li .destroy:hover,\n.todo-list li .destroy:focus {\n\tcolor: #C18585;\n}\n\n.todo-list li .destroy:after {\n\tcontent: '×';\n\tdisplay: block;\n\theight: 100%;\n\tline-height: 1.1;\n}\n\n.todo-list li:hover .destroy {\n\tdisplay: block;\n}\n\n.todo-list li .edit {\n\tdisplay: none;\n}\n\n.todo-list li.editing:last-child {\n\tmargin-bottom: -1px;\n}\n\n.footer {\n\tpadding: 10px 15px;\n\theight: 20px;\n\ttext-align: center;\n\tfont-size: 15px;\n\tborder-top: 1px solid #e6e6e6;\n}\n\n.footer:before {\n\tcontent: '';\n\tposition: absolute;\n\tright: 0;\n\tbottom: 0;\n\tleft: 0;\n\theight: 50px;\n\toverflow: hidden;\n\tbox-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),\n\t 0 8px 0 -3px #f6f6f6,\n\t 0 9px 1px -3px rgba(0, 0, 0, 0.2),\n\t 0 16px 0 -6px #f6f6f6,\n\t 0 17px 2px -6px rgba(0, 0, 0, 0.2);\n}\n\n.todo-count {\n\tfloat: left;\n\ttext-align: left;\n}\n\n.todo-count strong {\n\tfont-weight: 300;\n}\n\n.filters {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tposition: absolute;\n\tright: 0;\n\tleft: 0;\n}\n\n.filters li {\n\tdisplay: inline;\n}\n\n.filters li a {\n\tcolor: inherit;\n\tmargin: 3px;\n\tpadding: 3px 7px;\n\ttext-decoration: none;\n\tborder: 1px solid transparent;\n\tborder-radius: 3px;\n}\n\n.filters li a:hover {\n\tborder-color: #DB7676;\n}\n\n.filters li a.selected {\n\tborder-color: #CE4646;\n}\n\n.clear-completed,\nhtml .clear-completed:active {\n\tfloat: right;\n\tposition: relative;\n\tline-height: 19px;\n\ttext-decoration: none;\n\tcursor: pointer;\n}\n\n.clear-completed:hover {\n\ttext-decoration: underline;\n}\n\n.info {\n\tmargin: 65px auto 0;\n\tcolor: #4d4d4d;\n\tfont-size: 11px;\n\ttext-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);\n\ttext-align: center;\n}\n\n.info p {\n\tline-height: 1;\n}\n\n.info a {\n\tcolor: inherit;\n\ttext-decoration: none;\n\tfont-weight: 400;\n}\n\n.info a:hover {\n\ttext-decoration: underline;\n}\n\n/*\n\tHack to remove background from Mobile Safari.\n\tCan't use it globally since it destroys checkboxes in Firefox\n*/\n@media screen and (-webkit-min-device-pixel-ratio:0) {\n\t.toggle-all,\n\t.todo-list li .toggle {\n\t\tbackground: none;\n\t}\n\n\t.todo-list li .toggle {\n\t\theight: 40px;\n\t}\n}\n\n@media (max-width: 430px) {\n\t.footer {\n\t\theight: 50px;\n\t}\n\n\t.filters {\n\t\tbottom: 10px;\n\t}\n}\n\n:focus,\n.toggle:focus + label,\n.toggle-all:focus + label {\n\tbox-shadow: 0 0 2px 2px #CF7D7D;\n\toutline: 0;\n}\n","@import '~todomvc-app-css/index.css';\n\n.description {\n padding: 15px;\n margin-bottom: 10px;\n ;\n}\n\ncode {\n display: block;\n padding: 15px;\n\n\n /**\n * https://css-tricks.com/better-line-breaks-for-long-urls/\n */\n overflow-wrap: break-word;\n word-wrap: break-word;\n word-break: break-word;\n hyphens: auto;\n}\n\na {\n color: #b83f45;\n font-weight: bold;\n text-decoration: none;\n}\n\na:visited {\n color: #b83f45;\n}\n\n\n/**\n * @link https://codepo8.github.io/css-fork-on-github-ribbon/\n */\n#forkongithub a {\n background: #000;\n color: #fff;\n text-decoration: none;\n font-family: arial, sans-serif;\n text-align: center;\n font-weight: bold;\n padding: 5px 40px;\n font-size: 1rem;\n line-height: 2rem;\n position: relative;\n transition: 0.5s;\n}\n\n#forkongithub a:hover {\n background: #b83f45;\n color: #fff;\n}\n\n#forkongithub a::before,\n#forkongithub a::after {\n content: \"\";\n width: 100%;\n display: block;\n position: absolute;\n top: 1px;\n left: 0;\n height: 1px;\n background: #fff;\n}\n\n#forkongithub a::after {\n bottom: 1px;\n top: auto;\n}\n\n@media screen and (min-width:800px) {\n #forkongithub {\n position: fixed;\n display: block;\n top: 0;\n right: 0;\n width: 200px;\n overflow: hidden;\n height: 200px;\n z-index: 9999;\n }\n\n #forkongithub a {\n width: 200px;\n position: absolute;\n top: 60px;\n right: -60px;\n transform: rotate(45deg);\n -webkit-transform: rotate(45deg);\n -ms-transform: rotate(45deg);\n -moz-transform: rotate(45deg);\n -o-transform: rotate(45deg);\n box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8);\n }\n}\n"],"names":[],"sourceRoot":""} -------------------------------------------------------------------------------- /files/p2p-todo-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubkey/rxdb-quickstart/1584560914f942149f1e9cb774f04691bc68b33c/files/p2p-todo-demo.gif -------------------------------------------------------------------------------- /files/p2p-todo-demo.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pubkey/rxdb-quickstart/1584560914f942149f1e9cb774f04691bc68b33c/files/p2p-todo-demo.webm -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rxdb-quickstart", 3 | "version": "0.0.0", 4 | "description": "A simple local-first p2p todo app to learn about RxDB", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack serve", 8 | "build": "rimraf -rf ./docs && webpack --mode=production", 9 | "disc": "cross-env NODE_ENV=disc webpack --config ./webpack.config.js", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/pubkey/rxdb-quickstart.git" 15 | }, 16 | "keywords": [ 17 | "rxdb" 18 | ], 19 | "author": "pubkey", 20 | "license": "Apache-2.0", 21 | "bugs": { 22 | "url": "https://github.com/pubkey/rxdb-quickstart/issues" 23 | }, 24 | "homepage": "https://github.com/pubkey/rxdb-quickstart#readme", 25 | "dependencies": { 26 | "rxdb": "16.13.0", 27 | "rxjs": "7.8.2", 28 | "todomvc-app-css": "2.4.3" 29 | }, 30 | "devDependencies": { 31 | "@types/node": "22.15.29", 32 | "@types/webpack": "5.28.5", 33 | "clean-webpack-plugin": "4.0.0", 34 | "copy-webpack-plugin": "13.0.0", 35 | "cross-env": "7.0.3", 36 | "css-loader": "7.1.2", 37 | "declaration-bundler-webpack-plugin": "1.0.3", 38 | "html-webpack-plugin": "5.6.3", 39 | "mini-css-extract-plugin": "2.9.2", 40 | "process": "0.11.10", 41 | "rimraf": "6.0.1", 42 | "ts-loader": "9.5.2", 43 | "typescript": "5.8.3", 44 | "webpack": "5.99.9", 45 | "webpack-bundle-analyzer": "4.10.2", 46 | "webpack-cli": "6.0.1", 47 | "webpack-dev-server": "5.2.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "statusCheckVerify": true, 6 | "ignoreDeps": [], 7 | "automerge": true, 8 | "major": { 9 | "automerge": true 10 | }, 11 | "ignorePaths": [], 12 | "rebaseStalePrs": true, 13 | "prConcurrentLimit": 6, 14 | "prHourlyLimit": 2, 15 | "dependencyDashboard": false 16 | } 17 | -------------------------------------------------------------------------------- /src/database.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RxCollection, 3 | createRxDatabase, 4 | defaultHashSha256, 5 | addRxPlugin, 6 | randomToken, 7 | RxDocument, 8 | RxJsonSchema, 9 | deepEqual, 10 | RxConflictHandler, 11 | RXDB_VERSION, 12 | RxStorage 13 | } from 'rxdb/plugins/core'; 14 | import { replicateWebRTC, getConnectionHandlerSimplePeer, SimplePeer } from 'rxdb/plugins/replication-webrtc'; 15 | import { getRxStorageDexie } from 'rxdb/plugins/storage-dexie'; 16 | 17 | export type TodoDocType = { 18 | id: string; 19 | name: string; 20 | state: 'open' | 'done'; 21 | lastChange: number; 22 | } 23 | export type RxTodoDocument = RxDocument; 24 | 25 | // set by webpack as global 26 | declare var mode: 'production' | 'development'; 27 | console.log('mode: ' + mode); 28 | 29 | let storage: RxStorage = getRxStorageDexie(); 30 | 31 | export const databasePromise = (async () => { 32 | // import dev-mode plugins 33 | if (mode === 'development') { 34 | await import('rxdb/plugins/dev-mode').then( 35 | module => addRxPlugin(module.RxDBDevModePlugin) 36 | ); 37 | await import('rxdb/plugins/validate-ajv').then( 38 | module => { 39 | storage = module.wrappedValidateAjvStorage({ storage }); 40 | } 41 | ); 42 | } 43 | 44 | // ensure roomId exists 45 | const roomId = window.location.hash; 46 | if (!roomId || roomId.length < 5) { 47 | window.location.hash = 'room-' + randomToken(10); 48 | window.location.reload(); 49 | } 50 | const roomHash = await defaultHashSha256(roomId); 51 | const database = await createRxDatabase<{ 52 | todos: RxCollection 53 | }>({ 54 | name: 'tpdp-' + RXDB_VERSION.replace(/\./g, '-') + '-' + roomHash.substring(0, 10), 55 | storage 56 | }); 57 | 58 | // handle replication conflicts (keep the document with the newest timestamp) 59 | const conflictHandler: RxConflictHandler = { 60 | isEqual(a, b) { 61 | return deepEqual( 62 | a, 63 | b 64 | ); 65 | }, 66 | resolve(input) { 67 | const ret = input.newDocumentState.lastChange > input.realMasterState.lastChange 68 | ? input.newDocumentState 69 | : input.realMasterState; 70 | return Promise.resolve(ret); 71 | } 72 | }; 73 | await database.addCollections({ 74 | todos: { 75 | schema: { 76 | version: 0, 77 | primaryKey: 'id', 78 | type: 'object', 79 | properties: { 80 | id: { 81 | type: 'string', 82 | maxLength: 20 83 | }, 84 | name: { 85 | type: 'string' 86 | }, 87 | state: { 88 | type: 'string', 89 | enum: [ 90 | 'open', 91 | 'done' 92 | ], 93 | maxLength: 10 94 | }, 95 | lastChange: { 96 | type: 'number', 97 | minimum: 0, 98 | maximum: 2701307494132, 99 | multipleOf: 1 100 | } 101 | }, 102 | required: ['id', 'name', 'state', 'lastChange'], 103 | indexes: [ 104 | 'state', 105 | ['state', 'lastChange'] 106 | ] 107 | } as RxJsonSchema, 108 | conflictHandler 109 | } 110 | }); 111 | database.todos.preSave(d => { 112 | d.lastChange = Date.now(); 113 | return d; 114 | }, true); 115 | await database.todos.bulkInsert( 116 | [ 117 | 'touch your 👃 with your 👅', 118 | 'solve rubik\'s cube 🎲 blindfolded', 119 | 'invent new 🍔' 120 | ].map((name, idx) => ({ 121 | id: 'todo-' + idx, 122 | name, 123 | lastChange: 0, 124 | state: 'open' 125 | })) 126 | ); 127 | replicateWebRTC({ 128 | collection: database.todos, 129 | connectionHandlerCreator: getConnectionHandlerSimplePeer({}), 130 | topic: roomHash.substring(0, 10), 131 | pull: {}, 132 | push: {}, 133 | }).then(replicationState => { 134 | replicationState.error$.subscribe((err: any) => { 135 | console.log('replication error:'); 136 | console.dir(err); 137 | }); 138 | replicationState.peerStates$.subscribe(s => { 139 | console.log('new peer states:'); 140 | console.dir(s); 141 | }); 142 | }); 143 | return database; 144 | })(); 145 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | RxDB Quickstart • TodoMVC 10 | 11 | 12 | 13 | 14 | 15 | Fork me on GitHub 19 | 20 | 21 |
      22 |
      23 |

      p2p todos

      24 |

      25 | This is a local first todo app that stores data locally with RxDB and 32 | replicates it peer-to-peer with WebRTC to other devices without sending 36 | the data through any central server. 37 | 38 | Open this url in another browser/device/tab to test the replication:
      39 | 40 |
      41 | The whole app is implemented without a framework in about 200 lines of TypeScript code. 42 | To learn more about how it works, I recommend looking at the source code and read the RxDB Quickstart Guide. 49 | 50 |

      51 |
      52 | 58 |
      59 |
      60 | 65 | 66 |
        70 | 71 |
      72 |
      73 | 74 |
        75 | 78 | 81 |
      82 | 86 |
      87 |
      88 |
      89 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ensureNotFalsy, 3 | randomToken 4 | } from 'rxdb/plugins/core'; 5 | import { 6 | RxTodoDocument, 7 | databasePromise 8 | } from './database.js'; 9 | import './style.css'; 10 | 11 | (async () => { 12 | const database = await databasePromise; 13 | 14 | // update url in description text 15 | getById('copy-url').innerHTML = window.location.href; 16 | 17 | // render reactive todo list 18 | const $todoList = getById('todo-list'); 19 | database.todos.find({ 20 | sort: [ 21 | { state: 'desc' }, 22 | { lastChange: 'desc' } 23 | ] 24 | }).$.subscribe(todos => { 25 | $todoList.innerHTML = ''; 26 | todos.forEach(todo => $todoList.append(getHtmlByTodo(todo))); 27 | }); 28 | 29 | // event: add todo 30 | const $insertInput = getById('insert-todo'); 31 | const addTodo = async () => { 32 | if ($insertInput.value.length < 1) { return; } 33 | await database.todos.insert({ 34 | id: randomToken(10), 35 | name: $insertInput.value, 36 | state: 'open', 37 | lastChange: Date.now() 38 | }); 39 | $insertInput.value = ''; 40 | }; 41 | $insertInput.onkeydown = async (event) => { 42 | if (isEnterEvent(event)) { addTodo(); } 43 | } 44 | $insertInput.onblur = () => addTodo(); 45 | 46 | // event: clear completed 47 | getById('clear-completed').onclick = () => database.todos.find({ selector: { state: 'done' } }).remove(); 48 | })(); 49 | 50 | function getById(id: string): T { return ensureNotFalsy(document.getElementById(id)) as any; } 51 | const escapeForHTML = (s: string) => s.replace(/[&<]/g, c => c === '&' ? '&' : '<'); 52 | const isEnterEvent = (ev: KeyboardEvent) => ev.code === 'Enter' || ev.keyCode === 13; 53 | function getHtmlByTodo(todo: RxTodoDocument): HTMLLIElement { 54 | const $liElement = document.createElement('li'); 55 | const $viewDiv = document.createElement('div'); 56 | const $checkbox = document.createElement('input'); 57 | const $label = document.createElement('label'); 58 | const $deleteButton = document.createElement('button'); 59 | $liElement.append($viewDiv); 60 | $viewDiv.append($checkbox); 61 | $viewDiv.append($label); 62 | $viewDiv.append($deleteButton); 63 | 64 | // event: toggle todo state 65 | $checkbox.onclick = () => todo.incrementalPatch({ state: todo.state === 'done' ? 'open' : 'done' }); 66 | $checkbox.type = 'checkbox'; 67 | $checkbox.classList.add('toggle'); 68 | 69 | // event: change todo name 70 | $label.contentEditable = 'true'; 71 | const updateName = async () => { 72 | let newName = $label.innerText || $label.textContent as string; 73 | newName = newName.replace(/
      /g, '').replace(/\ /g, ' ').trim(); 74 | if (newName !== todo.name) { 75 | await todo.incrementalPatch({ name: newName }); 76 | } 77 | } 78 | $label.onblur = () => updateName(); 79 | $label.onkeyup = async (ev) => { 80 | if (isEnterEvent(ev)) { 81 | updateName(); 82 | } 83 | } 84 | $label.innerHTML = escapeForHTML(todo.name); 85 | 86 | // event: delete todo 87 | $deleteButton.classList.add('destroy'); 88 | $deleteButton.onclick = () => todo.remove(); 89 | 90 | if (todo.state === 'done') { 91 | $liElement.classList.add('completed'); 92 | $checkbox.checked = true; 93 | } 94 | 95 | return $liElement; 96 | } 97 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | @import '~todomvc-app-css/index.css'; 2 | 3 | .description { 4 | padding: 15px; 5 | margin-bottom: 10px; 6 | ; 7 | } 8 | 9 | code { 10 | display: block; 11 | padding: 15px; 12 | 13 | 14 | /** 15 | * https://css-tricks.com/better-line-breaks-for-long-urls/ 16 | */ 17 | overflow-wrap: break-word; 18 | word-wrap: break-word; 19 | word-break: break-word; 20 | hyphens: auto; 21 | } 22 | 23 | a { 24 | color: #b83f45; 25 | font-weight: bold; 26 | text-decoration: none; 27 | } 28 | 29 | a:visited { 30 | color: #b83f45; 31 | } 32 | 33 | 34 | /** 35 | * @link https://codepo8.github.io/css-fork-on-github-ribbon/ 36 | */ 37 | #forkongithub a { 38 | background: #000; 39 | color: #fff; 40 | text-decoration: none; 41 | font-family: arial, sans-serif; 42 | text-align: center; 43 | font-weight: bold; 44 | padding: 5px 40px; 45 | font-size: 1rem; 46 | line-height: 2rem; 47 | position: relative; 48 | transition: 0.5s; 49 | } 50 | 51 | #forkongithub a:hover { 52 | background: #b83f45; 53 | color: #fff; 54 | } 55 | 56 | #forkongithub a::before, 57 | #forkongithub a::after { 58 | content: ""; 59 | width: 100%; 60 | display: block; 61 | position: absolute; 62 | top: 1px; 63 | left: 0; 64 | height: 1px; 65 | background: #fff; 66 | } 67 | 68 | #forkongithub a::after { 69 | bottom: 1px; 70 | top: auto; 71 | } 72 | 73 | @media screen and (min-width:800px) { 74 | #forkongithub { 75 | position: fixed; 76 | display: block; 77 | top: 0; 78 | right: 0; 79 | width: 200px; 80 | overflow: hidden; 81 | height: 200px; 82 | z-index: 9999; 83 | } 84 | 85 | #forkongithub a { 86 | width: 200px; 87 | position: absolute; 88 | top: 60px; 89 | right: -60px; 90 | transform: rotate(45deg); 91 | -webkit-transform: rotate(45deg); 92 | -ms-transform: rotate(45deg); 93 | -moz-transform: rotate(45deg); 94 | -o-transform: rotate(45deg); 95 | box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./docs/", 4 | "noImplicitAny": true, 5 | "module": "es2022", 6 | "target": "es5", 7 | "jsx": "react", 8 | "allowJs": true, 9 | "strict": true, 10 | "moduleResolution": "node", 11 | "sourceMap": true 12 | }, 13 | "include": [ 14 | "src" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 5 | const TerserPlugin = require('terser-webpack-plugin'); 6 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 7 | 8 | 9 | let mode = 'development'; 10 | const plugins = [ 11 | new webpack.ProvidePlugin({ 12 | process: 'process/browser', 13 | }), 14 | new HtmlWebpackPlugin({ 15 | template: 'src/index.html' 16 | }), 17 | new MiniCssExtractPlugin({ filename: 'src/style.css' }), 18 | ]; 19 | if (process.env.NODE_ENV === 'disc') { 20 | mode = 'production'; 21 | plugins.push(new BundleAnalyzerPlugin()); 22 | } 23 | if (process.argv.join(',').includes('mode=production')) { 24 | mode = 'production'; 25 | } 26 | 27 | plugins.push(new webpack.DefinePlugin({ mode: JSON.stringify(mode) })); 28 | 29 | module.exports = { 30 | entry: './src/index.ts', 31 | devtool: 'source-map', 32 | mode, 33 | module: { 34 | rules: [ 35 | { 36 | test: /\.tsx?$/, 37 | use: 'ts-loader', 38 | exclude: /node_modules/, 39 | }, 40 | { 41 | test: /\.css$/, 42 | use: [MiniCssExtractPlugin.loader, 'css-loader'] 43 | }, 44 | /** 45 | * @link https://github.com/react-dnd/react-dnd/issues/3425#issuecomment-1177329608 46 | */ 47 | { 48 | test: /\.m?js$/, 49 | resolve: { 50 | fullySpecified: false 51 | }, 52 | }, 53 | ], 54 | }, 55 | optimization: { 56 | minimize: true, 57 | minimizer: [ 58 | new TerserPlugin() 59 | ] 60 | }, 61 | resolve: { 62 | extensions: ['.tsx', '.ts', '.js'], 63 | }, 64 | output: { 65 | filename: '[contenthash].bundle.js', 66 | path: path.resolve(__dirname, 'docs'), 67 | }, 68 | resolve: { 69 | extensionAlias: { 70 | '.js': ['.js', '.ts'], 71 | } 72 | }, 73 | plugins 74 | }; 75 | --------------------------------------------------------------------------------