{"_id":"@alexstormwood/camo-multi","_rev":"6-6ba33ccdee14d9c315691ba81ec9a96e","name":"@alexstormwood/camo-multi","dist-tags":{"latest":"1.1.3"},"versions":{"1.0.0":{"name":"@alexstormwood/camo-multi","version":"1.0.0","description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","type":"module","main":"index.js","engines":{"node":">=14"},"scripts":{"test":"mocha --reporter spec --timeout 10000 test/","test:memory-nedb-original":"npm --nedb_version=original run test","test:memory-nedb-justlep":"npm --nedb_version=justlep run test","test:persistent-nedb-original":"npm --nedb_version=original --nedb_persistent=true run test","test:persistent-nedb-justlep":"npm --nedb_version=justlep --nedb_persistent=true run test","lint":"eslint --max-warnings 5 --ext .js index.js lib/ test/"},"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"homepage":"https://github.com/AlexStormwood/camo-multi","repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"license":"MIT","dependencies":{"depd":"^2.0.0"},"devDependencies":{"chai":"^4.3.6","eslint":"^8.16.0","eslint-plugin-log-filenames":"^1.0.6","mocha":"^10.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@alexstormwood/camo-multi@1.0.0","gitHead":"9f47e23223220cd9a83a312413db7ccc0dec860a","bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"_nodeVersion":"22.0.0","_npmVersion":"10.5.1","dist":{"integrity":"sha512-RgyUveFPoBYaG0xHHe5z1xbxWZweVzuckrzeEX8Sg8fe+tzEytLa5qnmAqvyPEh4hiQ3PWPauEIo/WsHsl6i/Q==","shasum":"b4e333459399c1f92f03b0d52f35c80bf27f3e1d","tarball":"https://registry.npmjs.org/@alexstormwood/camo-multi/-/camo-multi-1.0.0.tgz","fileCount":13,"unpackedSize":104822,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEVY7qudd9njaf/K2DpBoYcfMaFbRyp2HtBo2YHcEPh7AiABp7G+Nt5eN3j0ZfraRKi1933JDLUO2XaKOosf/EDn5Q=="}]},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/camo-multi_1.0.0_1714289578194_0.43359921127263834"},"_hasShrinkwrap":false,"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.1.0":{"name":"@alexstormwood/camo-multi","version":"1.1.0","description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","type":"module","main":"index.js","engines":{"node":">=14"},"scripts":{"test":"mocha --reporter spec --timeout 10000 test/","test:memory-nedb-original":"npm --nedb_version=original run test","test:memory-nedb-justlep":"npm --nedb_version=justlep run test","test:persistent-nedb-original":"npm --nedb_version=original --nedb_persistent=true run test","test:persistent-nedb-justlep":"npm --nedb_version=justlep --nedb_persistent=true run test","lint":"eslint --max-warnings 5 --ext .js index.js lib/ test/"},"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"homepage":"https://github.com/AlexStormwood/camo-multi","repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"license":"MIT","dependencies":{"depd":"^2.0.0"},"devDependencies":{"chai":"^4.3.6","eslint":"^8.16.0","eslint-plugin-log-filenames":"^1.0.6","mocha":"^10.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@alexstormwood/camo-multi@1.1.0","gitHead":"f11b0a9d9cf8c515d488d4bc8b065694adc6cf1f","bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"_nodeVersion":"22.0.0","_npmVersion":"10.5.1","dist":{"integrity":"sha512-DMqWgC9OlswVaG7kfj3GhSKXv+JG0Ay16jfu+Ifn985ND721ZR794xMWsP9k4JT7fuTNIJhhjOcqJFF6zhcA2A==","shasum":"963be267caba9ac5cac94f74c10f0b1345875b32","tarball":"https://registry.npmjs.org/@alexstormwood/camo-multi/-/camo-multi-1.1.0.tgz","fileCount":13,"unpackedSize":105199,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE84zemdVPD/OQvknkDZfltgNioLsiEjbLxHDhvzlt19AiEA/AYxh8aprXmLCtNXaLaOAJlKwxtA9+Cz21ewPdS1Ses="}]},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/camo-multi_1.1.0_1714290985803_0.6080667671997946"},"_hasShrinkwrap":false,"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.1.1":{"name":"@alexstormwood/camo-multi","version":"1.1.1","description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","type":"module","main":"index.js","engines":{"node":">=14"},"scripts":{"test":"mocha --reporter spec --timeout 10000 test/","test:memory-nedb-original":"npm --nedb_version=original run test","test:memory-nedb-justlep":"npm --nedb_version=justlep run test","test:persistent-nedb-original":"npm --nedb_version=original --nedb_persistent=true run test","test:persistent-nedb-justlep":"npm --nedb_version=justlep --nedb_persistent=true run test","lint":"eslint --max-warnings 5 --ext .js index.js lib/ test/"},"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"homepage":"https://github.com/AlexStormwood/camo-multi","repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"license":"MIT","dependencies":{"depd":"^2.0.0"},"devDependencies":{"chai":"^4.3.6","eslint":"^8.16.0","eslint-plugin-log-filenames":"^1.0.6","mocha":"^10.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@alexstormwood/camo-multi@1.1.1","gitHead":"6f9999ddf811b0aa7a371a0eb944b03ad346d6ca","bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"_nodeVersion":"22.0.0","_npmVersion":"10.5.1","dist":{"integrity":"sha512-7LOhw8KhF9fFzAq6/sHxsRRG2PLwClO8Xvhx8WSSrPmkQmE9APB9songGOIYqI49eVSOYfCFAMSKR4EYeiFHcg==","shasum":"333bedcfb6bd62f78ef011cf6c55c1a39672c5bd","tarball":"https://registry.npmjs.org/@alexstormwood/camo-multi/-/camo-multi-1.1.1.tgz","fileCount":13,"unpackedSize":106638,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICja2W1jrapwa7QcijZ1nGu0PKU/uXGgH6KoRcq2GgCKAiA8o2NMS0gS2Z9RyExSlgPLk7Y9C5UaflPApKNw4VbKEw=="}]},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/camo-multi_1.1.1_1714292671207_0.44869813864760566"},"_hasShrinkwrap":false,"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.1.2":{"name":"@alexstormwood/camo-multi","version":"1.1.2","description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","type":"module","main":"index.js","engines":{"node":">=14"},"scripts":{"test":"mocha --reporter spec --timeout 10000 test/","test:memory-nedb-original":"npm --nedb_version=original run test","test:memory-nedb-justlep":"npm --nedb_version=justlep run test","test:persistent-nedb-original":"npm --nedb_version=original --nedb_persistent=true run test","test:persistent-nedb-justlep":"npm --nedb_version=justlep --nedb_persistent=true run test","lint":"eslint --max-warnings 5 --ext .js index.js lib/ test/"},"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"homepage":"https://github.com/AlexStormwood/camo-multi","repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"license":"MIT","dependencies":{"depd":"^2.0.0"},"devDependencies":{"chai":"^4.3.6","eslint":"^8.16.0","eslint-plugin-log-filenames":"^1.0.6","mocha":"^10.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@alexstormwood/camo-multi@1.1.2","gitHead":"095d1a5dca381f6c4f57b08cec94986ea246d8d2","bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"_nodeVersion":"22.0.0","_npmVersion":"10.5.1","dist":{"integrity":"sha512-A/ITn7xnnzL6Rh6sUfiL+wS2Y1eU6NfVETYseErZYrhKvuBh9w/dhc4z0TZ30X4yCPXx18L/abcQAiQi3hgnLQ==","shasum":"d30b230199354b843f0c7330ea849ff2f29dc385","tarball":"https://registry.npmjs.org/@alexstormwood/camo-multi/-/camo-multi-1.1.2.tgz","fileCount":14,"unpackedSize":107723,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFooKScqJSZ915T3U5uLnTNp+Xvvynq0T1FF7R61WMbwIhAMAq52U4/TC51Q1KcXRB2RvjbT/6E3bKPvTnrY/xxAHT"}]},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/camo-multi_1.1.2_1714800521346_0.23302283580125605"},"_hasShrinkwrap":false,"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."},"1.1.3":{"name":"@alexstormwood/camo-multi","version":"1.1.3","description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","type":"module","main":"index.js","engines":{"node":">=14"},"scripts":{"test":"mocha --reporter spec --timeout 10000 test/","test:memory-nedb-original":"npm --nedb_version=original run test","test:memory-nedb-justlep":"npm --nedb_version=justlep run test","test:persistent-nedb-original":"npm --nedb_version=original --nedb_persistent=true run test","test:persistent-nedb-justlep":"npm --nedb_version=justlep --nedb_persistent=true run test","lint":"eslint --max-warnings 5 --ext .js index.js lib/ test/"},"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"homepage":"https://github.com/AlexStormwood/camo-multi","repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"license":"MIT","dependencies":{"depd":"^2.0.0"},"devDependencies":{"chai":"^4.3.6","eslint":"^8.16.0","eslint-plugin-log-filenames":"^1.0.6","mocha":"^10.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_id":"@alexstormwood/camo-multi@1.1.3","gitHead":"485baae5a2b61a1d41d16a053b958a6e2125d6eb","bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"_nodeVersion":"22.0.0","_npmVersion":"10.7.0","dist":{"integrity":"sha512-x8wfgWvvfvneS7rptPmad3lv5Ypu1GkKARQIBtjBYbb33rmo3xXd977fqwaRBYOsv8ImNF4v1iKLFE8EtRSMvg==","shasum":"180ff63fcd82b1997f8a64a148fbc914063e782b","tarball":"https://registry.npmjs.org/@alexstormwood/camo-multi/-/camo-multi-1.1.3.tgz","fileCount":14,"unpackedSize":108052,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHHm4XFbOkEeiyeEH0oj4VuqUNnJL0GpXT+QD9UTohsIAiAPJ+hy4eWCGDDowuh9NKuizhN68er/nZN9QsbLVpWfEA=="}]},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/camo-multi_1.1.3_1716038271421_0.35691211849176807"},"_hasShrinkwrap":false,"deprecated":"Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."}},"time":{"created":"2024-04-28T07:32:58.102Z","1.0.0":"2024-04-28T07:32:58.385Z","modified":"2024-05-18T13:18:14.573Z","1.1.0":"2024-04-28T07:56:25.973Z","1.1.1":"2024-04-28T08:24:31.375Z","1.1.2":"2024-05-04T05:28:41.551Z","1.1.3":"2024-05-18T13:17:51.623Z"},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"description":"A class-based Object-Document Mapper (ODM) for NeDB, with support for disconnecting and reconnecting to databases during an app's runtime.","homepage":"https://github.com/AlexStormwood/camo-multi","keywords":["es6","odm","nedb","nosql","document","model","schema","database","data","datastore","query","db"],"repository":{"type":"git","url":"git+https://github.com/AlexStormwood/camo-multi.git"},"contributors":[{"name":"Lennart Pegel","email":"github@justlep.net","url":"https://github.com/justlep/camo"},{"name":"Scott Robinson","email":"s.w.robinson@gmail.com","url":"https://github.com/scottwrobinson/camo"}],"author":{"name":"Alex Stormwood","email":"alex@bigfootds.com","url":"https://github.com/AlexStormwood/camo-multi"},"bugs":{"url":"https://github.com/AlexStormwood/camo-multi/issues"},"license":"MIT","readme":"# DEPRECATED\r\n\r\nPlease use this package, which was built from the ground up to facilitate an app running multiple NeDB database connections _concurrently_ instead of sequentially:\r\n\r\n## [https://github.com/BigfootDS/supercamo](https://github.com/BigfootDS/supercamo)\r\n\r\n## npm i @bigfootds/supercamo\r\n\r\n<hr />\r\n<hr />\r\n<hr />\r\n\r\n# Oh god, another fork?!\r\n\r\nI know, I know.\r\n\r\nGitHub user `justlep` has a great fork, but I need to be able to reset the database client connection at certain points in the app.\r\n\r\nI barely have my head wrapped around the original Camo, and this fork - so I'm sure this will not be a reliable one to use.\r\n\r\nI would appreciate the changes in this fork being pulled into a more-maintained repo - I really have no intention of doing anything more to this once the \"multi connection\" stuff that I need is implemented.\r\n\r\n## So, what does \"multi\" mean here?\r\n\r\nBasically, you are not locked into the first database that you connect to when using your app, in this version of Camo.\r\n\r\nYou can call a `.close()` function on a client, disconnecting your current client.\r\n\r\nThen, you can connect to another database.\r\n\r\nMulti!\r\n\r\nI'm using this in my work to have one NeDB database per game save. \r\n\r\nPlayers can play in World A, World A has a database for its persisted data. \r\n\r\nPlayer can end their session in World A, and start up a new session in World B. World B has its own database, no data shared between World A and World B.\r\n\r\nThis does mean that:\r\n\r\n- you must be aware of which database is active/connected\r\n- you must manually close your database and connect your database before doing database operations\r\n- you _should_ check for the current connected client instance to aid with the above\r\n- you _should_ have some seed data process if you do need to share any data between databases\r\n\r\n# A completely reworked fork of scottwrobinson's [camo](https://github.com/scottwrobinson/camo)\r\n\r\n\r\nChanges: \r\n* Complete code refactoring & optimization\r\n* allows using any fork of NeDB, like [@justlep/nedb](https://github.com/justlep/nedb)\r\n  ```javascript\r\n  import {Datastore} from '@justlep/nedb';\r\n  import {connect} from '@justlep/camo';\r\n  \r\n  await connect('nedb:///path/to/dbfiles', Datastore);\r\n  \r\n  // The Datastore argument is now mandatory.\r\n  // For using the original 'nedb@1.8.0', you would do\r\n  \r\n  import Datastore from 'nedb'; \r\n  await connect('nedb:///path/to/dbfiles', Datastore);\r\n  ```\r\n* analog to the `\"unique\"` flag in Document schemas (ensuring a unique index for a field), there is now an \r\n  additional `\"indexed\"` flag ensuring a non-unique index for the respective field, e.g.\r\n   ```javascript\r\n  class Book extends Document {\r\n    static SCHEMA = {\r\n      isbn: {\r\n        type: String,\r\n        unique: true        \r\n      },\r\n      year: {\r\n        type: Number,\r\n        indexed: true\r\n      }\r\n    }\r\n  }\r\n  ``` \r\n  \r\n**Breaking changes:**\r\n* Removed Mongo support for now, leaving NeDB only\r\n* Removed `nedb` from `optionalDependencies`, must be installed manually:\r\n  ```sh\r\n  # for Node 14+/ESM (see code example above)\r\n  npm i --save @justlep/nedb \r\n  \r\n  # or the original but unmaintained NeDB v1.8.0\r\n  npm i --save nedb\r\n  ```\r\n* Accessing `id` properties of `Document` or `EmbeddedDocument` no longer displays deprecation warnings, but will throw an Error.\r\n* Passing a collection name to `new MyDocument(collectionName)` now throws an `Error` (must override `static collectionName()` instead)\r\n* Documents now throw errors when created with data objects containing keys that are\r\n  not present in the schema. This behavior can be <a href=\"#behavior-on-unknown-data-keys\">customized</a>. \r\n* Schema definition by constructor (or `instance.schema()`) is deprecated.\r\n  Use a static field SCHEMA instead, e.g.\r\n  ```javascript\r\n  class Foo extends Document {\r\n    static SCHEMA = {\r\n      num: {\r\n        type: Number,\r\n        default: 123\r\n      }\r\n    }\r\n  }\r\n  \r\n  Foo.create().num === 123; // true\r\n  ``` \r\n  More info below at <a href=\"#declaring-your-document\">Declaring Your Document</a>\r\n* `Client._dropDatabase()` is now private/internal (see CHANGELOG)\r\n\r\n# Camo\r\n\r\n\r\n## Jump To\r\n* <a href=\"#why-do-we-need-another-odm\">Why do we need another ODM?</a>\r\n* <a href=\"#advantages\">Advantages</a>\r\n* <a href=\"#install-and-run\">Install and Run</a>\r\n* <a href=\"#quick-start\">Quick Start</a>\r\n  * <a href=\"#connect-to-the-database\">Connect to the Database</a>\r\n  * <a href=\"#declaring-your-document\">Declaring Your Document</a>\r\n    * <a href=\"#embedded-documents\">Embedded Documents</a>\r\n  * <a href=\"#creating-and-saving\">Creating and Saving</a>\r\n  * <a href=\"#loading\">Loading</a>\r\n    * <a href=\"#behavior-on-unknown-data-keys\">Behavior on unknown data keys</a> \r\n  * <a href=\"#deleting\">Deleting</a>\r\n  * <a href=\"#counting\">Counting</a>\r\n  * <a href=\"#hooks\">Hooks</a>\r\n  * <a href=\"#misc\">Misc.</a>\r\n* <a href=\"#transpiler-support\">Transpiler Support</a>\r\n* <a href=\"#contributing\">Contributing</a>\r\n* <a href=\"#contact\">Contact</a>\r\n* <a href=\"#copyright-license\">Copyright & License</a>\r\n\r\n## Why do we need another ODM?\r\nShort answer, we probably don't. Camo was created for two reasons: ~~to bring traditional-style classes to [MongoDB](https://www.mongodb.com/) JavaScript, and~~ to support [NeDB](https://github.com/louischatriot/nedb) as a backend (which is much like the SQLite-alternative to Mongo).\r\n\r\nThroughout development this eventually turned in to a library full of [ES6](https://github.com/lukehoban/es6features) features. Coming from a Java background, its easier for me to design and write code in terms of classes, and I suspect this is true for many JavaScript beginners. While ES6 classes don't bring any new functionality to the language, they certainly do make it much easier to jump in to OOP with JavaScript, which is reason enough to warrent a new library, IMO.\r\n\r\n## Advantages\r\nSo, why use Camo?\r\n\r\n- **ES6**: ES6 features are quickly being added to Node, especially now that it has merged with io.js. With all of these new features being released, Camo is getting a head start in writing tested and proven ES6 code. This also means that native Promises are built-in to Camo, so no more `promisify`-ing your ODM or waiting for Promise support to be added natively.\r\n- **Easy to use**: While JavaScript is a great language overall, it isn't always the easiest for beginners to pick up. Camo aims to ease that transition by providing familiar-looking classes and a simple interface. Also, there is no need to install a full MongoDB instance to get started thanks to the support of NeDB.\r\n- **Multiple backends**: Camo was designed and built with multiple Mongo-like backends in mind, like NeDB, LokiJS\\*, and TaffyDB\\*. With NeDB support, for example, you don't need to install a full MongoDB instance for development or for smaller projects. This also allows you to use Camo in the browser, since databases like NeDB supports in-memory storage.\r\n- **Lightweight**: Camo is just a very thin wrapper around the backend databases, which mean you won't be sacrificing performance.\r\n\r\n\\* Support coming soon.\r\n\r\n## Install and Run\r\nTo use Camo, you must first have installed **Node >=14**, then run the following commands:\r\n\r\n    npm install @justlep/camo --save\r\n\r\nAnd at least ONE of the following:\r\n\r\n    npm install @justlep/nedb --save\r\n\r\n    OR\r\n\r\n    npm install nedb --save\r\n\r\n<!-- \r\n   (mongo-support dropped for now..)  \r\n   npm install mongodb --save \r\n-->\r\n\r\n## Quick Start\r\nCamo was built with ease-of-use and ES6 in mind, so you might notice it has more of an OOP feel to it than many existing libraries and ODMs. Don't worry, focusing on object-oriented design doesn't mean we forgot about functional techniques or asynchronous programming. Promises are built-in to the API. Just about every call you make interacting with the database (find, save, delete, etc) will return a Promise. No more callback hell :)\r\n\r\nFor a short tutorial on using Camo, check out [this](http://stackabuse.com/getting-started-with-camo/) article.\r\n\r\n### Connect to the Database\r\nBefore using any document methods, you must first connect to your underlying database. All supported databases have their own unique URI string used for connecting. The URI string usually describes the network location or file location of the database. However, some databases support more than just network or file locations. NeDB, for example, supports storing data in-memory, which can be specified to Camo via `nedb://memory`. See below for details:\r\n\r\n<!--\r\n- MongoDB: \r\n  - Format: mongodb://[username:password@]host[:port][/db-name]\r\n  - Example: `var uri = 'mongodb://scott:abc123@localhost:27017/animals';`\r\n-->\r\n- NeDB:\r\n  - Format: `\"nedb://[directory-path]\"` OR `\"nedb://memory\"`\r\n  - Example: `const uri = 'nedb:///Users/scott/data/db-files';`\r\n\r\nSo to connect to an NeDB database, use the following:\r\n\r\n```javascript\r\nimport {Datastore} from '@justlep/nedb';\r\nimport {connect} from '@justlep/camo';\r\n\r\nconst database = await await connect('nedb:///path/to/dbfiles', Datastore);\r\n  \r\n// The Datastore argument is now mandatory.\r\n// For using the original 'nedb@1.8.0', you would do\r\n  \r\nimport Datastore from 'nedb'; \r\nconst database = await connect('nedb:///path/to/dbfiles', Datastore);\r\n  ```\r\n\r\n\r\n### Declaring Your Document\r\nAll models must inherit from the `Document` class, which handles much of the interface to your backend NoSQL database.\r\n\r\n```javascript\r\nimport {Document} from '@justlep/camo';\r\n\r\nclass Company extends Document {\r\n    static SCHEMA = {\r\n        name: String,\r\n        valuation: {\r\n            type: Number,\r\n            default: 10000000000,\r\n            min: 0\r\n        },\r\n        employees: [String],\r\n        dateFounded: {\r\n            type: Date,\r\n            default: Date.now\r\n        }\r\n    };\r\n\r\n    static collectionName() {\r\n        return 'companies';\r\n    }\r\n}\r\n```\r\n\r\nThe schema is defined by a static `SCHEMA` property. \r\nSchemas are analyzed **once per class** at the moment the first instance of the class (or a derived class) gets instantiated. \r\nChanging `SCHEMA` thereafter will have no effect.  \r\n\r\n\r\nThe name of the collection can be set by overriding the `static collectionName()` method, which should return the desired collection name as a string. If one isn't given, then Camo uses the name of the class and naively appends an 's' to the end to make it plural.\r\n\r\nThe `SCHEMA` property can also be a function returning a schema object. \r\nThis allows for circular document-type references. For example:\r\n\r\n```javascript\r\nclass Foo extends Document {\r\n    static SCHEMA = () => ({bar: Bar});\r\n}\r\n\r\nclass Bar extends Document {\r\n    static SCHEMA = {foo: Foo};\r\n}\r\n```\r\n\r\n\r\nSupported variable types are:\r\n\r\n- `String`\r\n- `Number`\r\n- `Boolean`\r\n- `Buffer`\r\n- `Date`\r\n- (`Object`)\r\n- (`Array`)\r\n- `EmbeddedDocument`\r\n- Document Reference\r\n\r\nArrays either can be declared as either un-typed (using `Array` or `[]`), or typed (using the `[TYPE]` \r\nsyntax, like `[String]`). Typed arrays are enforced by Camo on `.save()` and an `Error` will be\r\nthrown if a value of the wrong type is saved in the array. Arrays of references are also supported.\r\n\r\n### Untyped arrays and \"wildcard\" objects\r\n\r\nUntyped arrays and `Object`-type fields require additional schema properties `fromData`, `toData` and `validate` for \r\nsanitizing raw data to be stored to or restored from the database, and for validating the value before `save():`  \r\n\r\nExample:\r\n\r\n```javascript\r\n// the 'obj' property of these instances are saved as strings in the db \r\nclass CustomObjectModel extends Document {\r\n    static SCHEMA = {\r\n        obj: {\r\n            type: Object,\r\n            toData: JSON.stringify, // converts the current value into a format the db can save  \r\n            fromData: JSON.parse,   // parses the value coming from the database  \r\n            validate: (o) => o && typeof o === 'object' && o.hi === 'bye'\r\n        }\r\n    };\r\n}\r\n```\r\n\r\n### Declaring a member variable in the schema\r\nTo declare a member variable in the schema, either directly assign it one of the types listed above, or assign it an object with options, like this:\r\n\r\n\r\n```javascript\r\nclass Foo extends Document {\r\n    static SCHEMA = {\r\n        anyNumber: Number,\r\n        primeNumber: {\r\n            type: Number,\r\n            default: 2,\r\n            min: 0,\r\n            max: 25,\r\n            choices: [2, 3, 5, 7, 11, 13, 17, 19, 23],\r\n            unique: true\r\n        }\r\n    };\r\n}\r\n```\r\n\r\nThe `default` option supports both values and no-argument functions (like `Date.now`). Currently the supported options/validators are:\r\n\r\n- `type`: The value's type *(required)*\r\n- `default`: The value to be assigned if none is provided *(optional)*\r\n- `min`: The minimum value a Number can be *(optional)*\r\n- `max`: The maximum value a Number can be *(optional)*\r\n- `choices`: A list of possible values *(optional)*\r\n- `match`: A regex string that should match the value *(optional)*\r\n- `validate`: A 1-argument function that returns `false` if the value is invalid *(optional)*\r\n- `unique`: A boolean value indicating if a 'unique' index should be set *(optional)*\r\n- `required`: A boolean value indicating if a key value is required *(optional)*\r\n\r\nTo reference another document, just use its class name as the type.\r\n\r\n```javascript\r\nclass Dog extends Document {\r\n    static SCHEMA = {\r\n        name: String,\r\n        breed: String\r\n    };  \r\n}\r\n\r\nclass Person extends Document {\r\n    static SCHEMA = {\r\n        pet: Dog,\r\n        name: String,\r\n        age: Number\r\n    };\r\n\r\n    static collectionName() {\r\n        return 'people';\r\n    }\r\n}\r\n```\r\n\r\n#### Embedded Documents\r\nEmbedded documents can also be used within `Document`s. You must declare them separately from the main `Document` that it is being used in. `EmbeddedDocument`s are good for when you need an `Object`, but also need enforced schemas, validation, defaults, hooks, and member functions. \r\nAll of the options (`type`, `default`, `min`, etc) mentioned above work on `EmbeddedDocument`s as well.\r\n\r\n```javascript\r\nimport {Document, EmbeddedDocument} from '@justlep/camo';\r\n\r\nclass Money extends EmbeddedDocument {\r\n    static SCHEMA = {\r\n        value: {\r\n            type: Number,\r\n            choices: [1, 5, 10, 20, 50, 100]\r\n        },\r\n        currency: {\r\n            type: String,\r\n            default: 'usd'\r\n        }\r\n    };\r\n}\r\n\r\nclass Wallet extends Document {\r\n    static SCHEMA = {\r\n        contents: [Money]\r\n    };\r\n}\r\n\r\nlet wallet = Wallet.create();\r\nwallet.contents.push(Money.create());\r\nwallet.contents[0].value = 5;\r\nwallet.contents.push(Money.create());\r\nwallet.contents[1].value = 100;\r\n\r\nawait wallet.save();\r\nconsole.log('Both Wallet and Money objects were saved!');\r\n````\r\n\r\n### Creating and Saving\r\nTo create a new instance of our document, we need to use the `.create()` method, which handles all of the construction for us.\r\n\r\n```javascript\r\nlet lassie = Dog.create({\r\n    name: 'Lassie',\r\n    breed: 'Collie'\r\n});\r\n\r\nlassie.save().then(function(l) {\r\n    console.log(l._id);\r\n});\r\n```\r\n\r\nOnce a document is saved, it will automatically be assigned a unique identifier by the backend database. This ID can be accessed by the `._id` property.\r\n\r\nIf you specified a default value (or function) for a schema variable, that value will be assigned on creation of the object.\r\n\r\nAn alternative to `.save()` is `.findOneAndUpdate(query, update, options)`. This static method will find and update (or insert) a document in one atomic operation (atomicity is guaranteed in MongoDB only). Using the `{upsert: true}` option will return a new document if one is not found with the given query.\r\n\r\n### Loading\r\nBoth the find and delete methods following closely (but not always exactly) to the MongoDB API, so it should feel fairly familiar.\r\n\r\nIf querying an object by `id`, you _must_ use `_id` and **not** `id`.\r\n\r\nTo retrieve an object, you have a few methods available to you.\r\n\r\n- `.findOne(query, options)` (static method)\r\n- `.find(query, options)` (static method)\r\n\r\nThe `.findOne()` method will return the first document found, even if multiple documents match the query. `.find()` will return all documents matching the query. Each should be called as static methods on the document type you want to load.\r\n\r\n```javascript\r\nlet lassie = await Dog.findOne({ name: 'Lassie' });\r\nconsole.log('Got Lassie!');\r\nconsole.log('Her unique ID is', lassie._id);\r\n```\r\n\r\n`.findOne()` currently accepts the following option:\r\n\r\n- `populate`: Boolean value to load all or no references. Pass an array of field names to only populate the specified references\r\n  - `Person.findOne({name: 'Billy'}, {populate: true})` populates all references in `Person` object\r\n  - `Person.findOne({name: 'Billy'}, {populate: ['address', 'spouse']})` populates only 'address' and 'spouse' in `Person` object\r\n\r\n`.find()` currently accepts the following options:\r\n\r\n- `populate`: Boolean value to load all or no references. Pass an array of field names to only populate the specified references\r\n  - `Person.find({lastName: 'Smith'}, {populate: true})` populates all references in `Person` object\r\n  - `Person.find({lastName: 'Smith'}, {populate: ['address', 'spouse']})` populates only 'address' and 'spouse' in `Person` object\r\n- `sort`: Sort the documents by the given field(s)\r\n  - `Person.find({}, {sort: '-age'})` sorts by age in descending order\r\n  - `Person.find({}, {sort: ['age', 'name']})` sorts by ascending age and then name, alphabetically\r\n- `limit`: Limits the number of documents returned\r\n  - `Person.find({}, {limit: 5})` returns a maximum of 5 `Person` objects\r\n- `skip`: Skips the given number of documents and returns the rest\r\n  - `Person.find({}, {skip: 5})` skips the first 5 `Person` objects and returns all others\r\n\r\n### Behavior on unknown data keys\r\n\r\nRefactorings in collections or Document classes may, over time, lead to NeDB data objects \r\ncontaining \"old\" properties which are no longer part of a document's schema. Since it can't be\r\nconsidered safe in all cases to silently ignore or accept such \"unknown data keys\", the default\r\nbehavior of `@justlep/camo` is now to throw an Error.\r\n\r\nThis behavior can be configured globally by calling `setUnknownDataKeyBehavior()`:\r\n```javascript\r\nimport {setUnknownDataKeyBehavior} from '@justlep/camo';\r\n\r\nsetUnknownDataKeyBehavior('throw');  // throw with error message containg key + class name\r\nsetUnknownDataKeyBehavior('ignore'); // silently ignore unknown data\r\nsetUnknownDataKeyBehavior('accept'); // silently accept \r\nsetUnknownDataKeyBehavior('logAndAccept'); // accept and log key + class name to the console \r\nsetUnknownDataKeyBehavior('logAndIgnore'); // ignore and log key + class name to the console\r\nsetUnknownDataKeyBehavior('default'); // switch back to default (=throw)  \r\n\r\n// or set global custom handlers (this-context will be the instance the data shall be assigned to) \r\nsetUnknownDataKeyBehavior(function(dataKey, dataVal) {\r\n    console.warn('unknonwn key %s for class %s', dataKey. this.constructor.name);\r\n    if (dataKey[0] !== '_') {\r\n        this[dataKey] = dataVal;\r\n    }\r\n});\r\n```\r\n\r\nAlternatively, you can override `onUnknownData()` for individual classes:\r\n```javascript\r\nclass Foo extends Document {\r\n  static SCHEMA = {};\r\n  \r\n  /** @override */\r\n  onUnknownData(dataKey, dataVal) {\r\n      this[dataKey] = dataVal; // silently accept unkown keys\r\n  }\r\n}\r\n\r\nlet foo = Foo.create({xxx: 666}); // won't throw\r\nfoo.xxx === 666; // true\r\n```\r\n\r\n**Note:** only document properties mentioned in the schema will be persisted, regardless of the settings above.\r\nAccepting random data keys does not mean those properties will be saved or updated in the database. \r\n\r\n\r\n### Deleting\r\nTo remove documents from the database, use one of the following:\r\n\r\n- `.delete()`\r\n- `.deleteOne(query, options)` (static method)\r\n- `.deleteMany(query, options)` (static method)\r\n- `.findOneAndDelete(query, options)` (static method)\r\n\r\nThe `.delete()` method should only be used on an instantiated document with a valid `id`. The other three methods should be used on the class of the document(s) you want to delete.\r\n\r\n```javascript\r\nlet numDeleted = await Dog.deleteMany({ breed: 'Collie' });\r\nconsole.log(`Deleted ${numDeleted} Collies from the database.`);\r\n```\r\n\r\n### Counting\r\nTo get the number of matching documents for a query without actually retrieving all of the data, use the `.count()` method.\r\n\r\n```javascript\r\nlet totalCollies = await Dog.count({ breed: 'Collie' });\r\nconsole.log(`Found ${totalCollies} Collies`);\r\n```\r\n\r\n### Hooks\r\nCamo provides hooks for you to execute code before and after critical parts of your database interactions. For each hook you use, you may return a value (which, as of now, will be discarded) or a Promise for executing asynchronous code. Using Promises throughout Camo allows us to not have to provide separate async and sync hooks, thus making your code simpler and easier to understand.\r\n\r\nHooks can be used not only on `Document` objects, but `EmbeddedDocument` objects as well. The embedded object's hooks will be called when it's parent `Document` is saved/validated/deleted (depending on the hook you provide).\r\n\r\nIn order to create a hook, you must override a class method. The hooks currently provided, and their corresponding methods, are:\r\n\r\n- pre-validate: `preValidate()`\r\n- post-validate: `postValidate()`\r\n- pre-save: `preSave()`\r\n- post-save: `postSave()`\r\n- pre-delete: `preDelete()`\r\n- post-delete: `postDelete()`\r\n\r\nHere is an example of using a hook (pre-delete, in this case):\r\n```javascript\r\nclass Company extends Document {\r\n    static SCHEMA = {\r\n        employees: [Person]\r\n    };\r\n\r\n    preDelete() {\r\n        return Promise.all(this.employees.map(emp => emp.delete()));\r\n    }\r\n}\r\n```\r\n\r\nThe code above shows a pre-delete hook that deletes all the employees of the company before it itself is deleted. As you can see, this is much more convenient than needing to always remember to delete referenced employees in the application code.\r\n\r\n**[!] Note**: The `.preDelete()` and `.postDelete()` hooks are _only_ called when calling `.delete()` on a Document instance. Calling `.deleteOne()` or `.deleteMany()` will **not** trigger the hook methods.\r\n\r\n### Misc.\r\n- `camo.getClient()`: Retrieves the Camo database client\r\n- `camo.getClient().driver()`: Retrieves the underlying database driver (`MongoClient` or a map of NeDB collections)\r\n- `Document.toJSON()`: Serializes the given document to just the data, which includes nested and referenced data\r\n\r\n## Transpiler Support\r\n_(This information may be obsolete)_\r\n\r\nWhile many transpilers won't have any problem with Camo, some need extra resources/plugins to work correctly:\r\n\r\n- Babel\r\n  - [babel-preset-camo](https://github.com/scottwrobinson/babel-preset-camo): Babel preset for all es2015 plugins supported by Camo\r\n- TypeScript\r\n  - [DefinitelyTyped/camo](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/camo): Camo declaration file (h/t [lucasmciruzzi](https://github.com/lucasmciruzzi))\r\n  - [IndefinitivelyTyped/camo](https://github.com/IndefinitivelyTyped/camo): Typings support for Camo (h/t [WorldMaker](https://github.com/WorldMaker))\r\n\r\n## Contributing\r\nFeel free to open new issues or submit pull requests for Camo. \r\n\r\nhttps://github.com/justlep/camo/issues\r\n\r\nBefore opening an issue or submitting a PR, I ask that you follow these guidelines:\r\n\r\n**Issues**\r\n- Please state whether your issue is a question, feature request, or bug report.\r\n- Always try the latest version of Camo before opening an issue.\r\n- If the issue is a bug, be sure to clearly state your problem, what you expected to happen, and what all you have tried to resolve it.\r\n- Always try to post simplified code that shows the problem. Use Gists for longer examples.\r\n\r\n**Pull Requests**\r\n- If your PR is a new feature, please consult with me first.\r\n- Any PR should contain only one feature or bug fix. If you have more than one, please submit them as separate PRs.\r\n- Always try to include relevant tests with your PRs. If you aren't sure where a test should go or how to create one, feel free to ask.\r\n- Include updates to the README when needed.\r\n- Do not update the package version or CHANGELOG. I'll handle that for each release.\r\n\r\n## Copyright & License\r\nCopyright (c) 2022 Lennart Pegel\r\n\r\nCopyright (c) 2016 Scott Robinson\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n","readmeFilename":"README.md"}