Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
To install Enmap, please read these instructions very carefully, every word is important!
Enmap is a wrapper around better-sqlite3, which requires to be built directly on your system. As such, you need to install pre-requisites first. Please follow these instructions to the letter. If it's not written here, you probably shouldn't do it unless you know why you're doing it.
SQLite modules usually only successfully work on LTS versions of node. This means it will work correctly on node 12, and 14. It will not work on node 13, 15, 17. Make sure you have the right version, check this with node -v
.
Also, better-sqlite3 doesn't compile on unreleased node versions usually. Your mileage may vary, but don't expect it to work on the "Latest" version of node either.
How to install the pre-requisites depends on your operating system, so see below for instructions:
On Windows, two things are required to install better-sqlite3. Python, and the Visual Studio C++ Build Tools. They are required for any module that is built on the system, which includes sqlite.
To install the necessary prerequisites on Windows, the easiest is to simply run the following commands separately, under an administrative command prompt or powershell:
It's very important that this be run in the administrative prompt, and not a regular one.
Once the windows-build-tools are installed (this might take quite some time, depending on your internet connection), close all open command prompts, powershell windows, and editors with a built-in console/prompt. Otherwise, the next command will not work.
On Linux, the pre-requisites are much simpler in a way. A lot of modern systems (such as Ubuntu, since 16.04) already come with python 2.7 pre-installed. For some other systems, you might have to fiddle with it to either get python 2.7 installed, or to install both 2.7 and 3.x simultaneously. Google will be your friend.
As for the C++ build tools, that's installed using the simple command: sudo apt-get install build-essential
for most debian-based systems. For others, look towards your package manager and specificall "GCC build tools". Your mileage may vary but hey, you're using Linux, you should know this stuff.
As of writing this page, MacOS versions seem to all come pre-built with Python 2.7 on the system. You will, however, need the C++ build tools.
Install XCode
Once XCode is installed, go to Preferences, Downloads, and install the Command Line Tools.
Once installed, you're ready to continue.
Once those pre-requisites are installed (if they're not, scroll up, and follow the instructions), and you've closed all open command prompts, open a new, normal (not-admin) command prompt or terminal in your project, then install Enmap using the following command:
This will take a few minutes, as it needs to build better-sqlite3 from source code, and then install enmap itself. Note that "a few minutes" can be 1 or 30 minutes, it really depends on your hardware and configuration.
If you get any errors, please see the Troubleshooting Guide. If the guide doesn't help, join the Discord (link at the top of this page).
Mostly, this documentation will be concentrating on the "persistent" version of enmap - the one where data is saved automatically.
If you don't want persistence, the only difference is how you initialize the enmap:
By default, Enmap saves only in memory and does not save anything to disk. To have persistent storage, you need to add some options. Enmaps with a "name" option will save, and there are additional options you can use to fine-tune the saving and loading features.
The following is a list of all options that are available in Enmap, when initializing it:
name
: A name for the enmap. Defines the table name in SQLite (the name is "cleansed" before use).
If an enmap has a name, it is considered persistent and will require better-sqlite-pool
to run.
If an enmap does not have a name, it is not persistent and any option related to database interaction is ignored (fetchAll, autoFetch, polling and pollingInterval).
fetchAll
: Defaults to true
, which means fetching all keys on load. Setting it to false
means that no keys are fetched, so it loads faster and uses less memory.
autoFetch
: Defaults to true
. When enabled, will automatically fetch any key that's requested using get, getProp, etc. This is a "synchronous" operation, which means it doesn't need any of this promise or callback use.
dataDir
: Defaults to ./data
. Determines where the sqlite files will be stored. Can be relative (to your project root) or absolute on the disk. Windows users , remember to escape your backslashes!
cloneLevel
: Defaults to deep
. Determines how objects and arrays are treated when inserting and retrieving from the database.
none
: Data is inserted by reference, meaning if you change it in the Enmap it changes outside, and vice versa. This should only be used in non-persistent enmaps if you know what you're doing!.
shallow
: Any object or array will be inserted as a shallow copy, meaning the first level is copied but sub-elements are inserted as references. This emulates Enmap 3's behavior, but is not recommended unless you know what you're doing.
deep
: Any object or array will be inserted and retrieved as a deep copy, meaning it is a completely different object. Since there is no chance of ever creating side-effects from modifying object, This is the recommended, and default, setting.
polling
: defaults to false
. Determines whether Enmap will attempt to retrieve changes from the database on a regular interval. This means that if another Enmap in another process modifies a value, this change will be reflected in ALL enmaps using the polling feature.
pollingInterval
: defaults to 1000
, polling every second. Delay in milliseconds to poll new data from the database. The shorter the interval, the more CPU is used, so it's best not to lower this. Polling takes about 350-500ms if no data is found, and time will grow with more changes fetched. In my tests, 15 rows took a little more than 1 second, every second.
"Anything that can go wrong will go wrong" - Murphy's Law
Please make sure to read the install page VERY CAREFULLY! This should solve all the issues you face. If not carry on, but don't say we didn't tell you.
Your computer has to install some programs and then build everything from source. This is going to take a while depending on your computer speed. Patience is key. We can't speed it up for you. It may look like its frozen but it is not.
Please make sure you read the install page first. If you can't find what your looking for you can join the discord here.
Looks like someone hasn't follows the installation instructions correctly...
Enmap, the super simple database wrapper with over a million downloads to date. Wrapping around better-sqlite3 with its warm embrace, it's the easiest way to save data in node for your first project!
Enmap stands for "Enhanced Map", and is a data structure based on the native JavaScript Map() structure with additional helper methods from the native Array() structure. Enmap also offers persistence, which means it will automatically save everything to save to it in a database, in the background, without any additional code or delays.
Enmap requires filesystem access. It DOES NOT WORK on Heroku, or other such systems that do not allow you to save data directly to disk.
It should also not be used on Repl.it where the data cannot be hidden (and will be public) or on *Glitch *which has been known to break Enmap's data persistence and lose data.
While there are other better-known systems that offer some features of Enmap, especially caching in memory, Enmap is targeted specifically to newer users of JavaScript that might not want to deal with complicated systems or database queries.
Here are some advantages of using Enmap:
Simple to Install: Enmap itself only requires a simple npm install
command to install and use, and a single line to initialize. See Installation for details.
Simple to Use: Basic Enmap usage can be completely done with 1-2 lines of initialization, and 3 commands, set(), get() and delete().
Very Fast: Since Enmap resides in memory, accessing its data is blazing fast (as fast as Map() is). Even with persistence, Enmap still accesses data from memory so you get it almost instantly.
Some disadvantages, compared to using a database connection directly:
More memory use: Since Enmap resides in memory and (by default) all its data is loaded when it starts, your entire data resides in RAM. When using a large amount of data on a low-end computer or VPS, this might be an issue for some users.
Limited power: You can have multiple Enmap "tables" loaded in your app, but they do not and cannot have relationships between them. Basically, one Enmap value can't refer to another value in another Enmap. This is something databases can be very good at, but due to the simplistic nature of Enmap, it's not possible here.
Lack of scalability: Enmap is great for small apps that require a simple key/value storage. However, a scalable app spread over multiple processes, shards, or clusters, will be severely limited by Enmap as it cannot update itself from the database on change - one process would not be aware of another process' changes.
This guide assists in migrating your data from Enmap 3 using Providers, to the latest version of enmap.
You do not need this page if you're new to Enmap or if you're starting a new project!
Upgrading to enmap v4 requires a little bit of migration, as Enmap 4 changed the internal method by which data is stored, slightly. To use this migration:
Make a copy of your current app in a new folder.
Create a new folder "on the same level" as your bot. Name it something like "migrate"
You should now have 3 folders. Something like mybots/coolbot
, mybots/coolbot-copy
, mybots/migrate/
In the migrate
folder, run npm i enmap@3.1.4 enmap-sqlite@latest
, as well as whatever source provider you need if it's not sqlite (in my example, npm i enmap-mongo@latest
You should now have something like the following image.
In the migrate
folder, create an index.js
and use the following script for migration. Note that it's an example, change the provider option to fit what you're actually using.
Very important: the "target" must be enmap-sqlite. Enmap v4 only supports an sqlite-backend.
From the migrate
folder, run node index.js
, which should correctly migrate your data.
If you're using enmap-sqlite already, you don't really need to do the entire thing above. Adding a single file called migrate.js
to your project folder, then running it with node migrate.js
will convert the format and then all you need is to modify the code for Enmap 4. Still, I recommend backing up your bot first. Just in case.
There is very little you need to change when moving to Enmap 4. The only changes that are required after migrating is the initialization of your Enmap which is now simpler.
If using Enmap.multi(), the change is just as simple:
The rest of your code (all interactions with Enmap) can remain the same - there should be no need to edit any of it.
Once your data is migrating and the code is changed, you can go ahead and install enmap version 4 through npm i enmap@latest
in your "new" bot folder (the target of the migration). This will take a few minutes (it needs to rebuild sqlite) and output that 4.0.x is now installed. Start the bot, and it should be working! If it doesn't, join the support server and we'll help you out ^_^.
Now that we have a functional Enmap structure (which we'll always refer to as myEnmap
), we're ready to start writing data to it, and getting data from it.
The code samples on this page assume that you have correctly initialized
myEnmap
In terms of Enmap, "writing", "adding" and "editing" data is essentially the same thing. When using the basic set()
method, if the key does not exist it's created, and if it does, it's modified.
Enmap supports pretty much all native JavaScript data types. However, it cannot support Class Instances directly. That means if you have, say, a "User" object or a "House" class, they cannot be stored here.
The usage for the set()
method is simple:
key
must be a string or integer. A key should be unique, otherwise it will be overwritten by new values using the same key.
value
must be a supported native data type as mentioned above.
Here are a few examples of writing simple data values:
Getting data back from an Enmap is just as simple as writing to it. All you need is the key
of what you want to retrieve, and you get its value back!
Removing data from Enmap is as simple as saving or retrieving. You can easily use the delete() method as such:
While Enmap is a Map enhanced with Array methods, Enmap also offers some enhanced array methods for the data stored inside of it. Talk about Arrayception!
So what do I mean by methods for your stored data? I mean that you can store arrays inside Enmap, and directly push, pull, add and remove from those arrays. There are methods to work both on direct arrays, as well as arrays stored inside of an object.
Let's take a look at three example entries in Enmap that we can use. The first is a direct array, the second is an array inside an object, the last is an array of objects.
There are two methods to push to an array, one for simple arrays and one for arrays inside objects. Pushing in an Enmap array is the same as a regular array push: it adds the element to the end of the array.
Similarly, you can remove from an array. With the normal path system, you can either remove via the index in the array, or remove simple strings. To remove a complex object, you'll need to use a function in the remove method.
What is Paths in Enmap, how to use them, what is their syntax?
In a whole lot of methods for Enmap, one of the properties is the "path". Paths are used in Object data saved in Enmap, that is to say, setting or ensuring a value that is an object at the top level.
To understand what a path really means, we can start by having an object as a value. Here I'm not even using Enmap, as the idea is related to basic JavaScript, not my module.
So here we have an object that actually has multiple levels, that is to say, the c
and sub
properties have, as a value, another object with its own keys. sub
takes this further with 4 different levels, just to fully demonstrate my point.
So how would we reach the values in this object? Well, in core JavaScript, let's say we wanted to get the word "cool", we'd use myObject.sub.values.are.cool
. This is one way to access object properties, the other one being myObject["sub"]["values"]["are"]["cool"]
(where those strings can be variables, btw, for dynamic property access).
Alright so what about the array, there? Well, arrays are accessed through their index, meaning their position in the array, starting at 0. That means to access the c.and
values, you'd do something like myObject.c.and[0]
. That looks like a strange syntax I'll admit, but considering you can use the same for objects, myObject["c"]["and"][1]
perhaps looks a bit more coherent.
Now that you've seen how to access those properties in regular JavaScript, what about doing it in Enmap? Well, it's actually quite simple: the path
parameter in the methods simply take exactly what you've seen above, with 2 exceptions:
The path doesn't include the object name (which is your key
)
You don't need to use variables for dynamic paths since it's a string
What does that mean in reality? Well let's rewrite the example above as Enmap code:
To access the "cool" string, the code then becomes myEnmap.get("myObject", "sub.values.are")
. Accessing the array values looks the same: myEnmap.get("myObject", "c.and[0]")
. In this case indexes can be used either way, so you can also do myEnmap.get("myObject", "c.and.0")
and that'll work equally well.
This page will describe how to use Enmap from multiple files within your same project. Note that I mean the same app, process, or shard, but different files within this one running process.
When Enmap is used with its default options, it loads everything in its cache and generally provides your data from this cache, not directly from the database. In the case where you want to use the data from one Enmap from multiple locations, you might encounter the following issue:
Hi! When I update data in Enmap from one file, it doesn't update in the other file, I have to restart the bot to update. Is this a bug?
However, this also means that when you do new Enmap({ name: "something" })
from more than one file, that's also a different instance, that doesn't share the same memory space. So not only will it not update the data in memory for the other file, it also uses double the memory. And of course, that's bad. So how do we fix this?
Admittedly, the vast majority of you Enmap users are doing Discord.js Bots, and even though Enmap works fine with any nodejs project that need simple data storage, bots are my main clients. Considering this fact, we have an extremely simple way to share an Enmap between multiple files: We attach it to the bot client. Usually your client is defined in your main file (index.js, app.js, bot.js, whatever you named it), and every part of your bot has access to this client. We can attach Enmap directly to it, like so:
This will work even if you're using a command handler, framework, or whatever - as long as you have access to a client variable, you have access to your enmaps.
In other frameworks and libraries, you might have something similar. For example with Express or Koa for http servers, you can sometimes attach the enmap to your request from the very top, in a middleware. If that's not possible, or if you find that to be complicated, you can use the next method.
This means you can simply require that file elsewhere. Let's say we called that file db.js
, here's how you'd use it:
And as I mentioned, as a bonus you now have the ability to create functions which you can export and use, to simplify your code and remove duplication. So, let's say I need to get all the tags for a specific guild, and my tags are built using an object as shown above. To get all those tags for a guild, you'd need filters, right? Like so:
now let's say you use this code a lot in your app, and you'd like to not have to type this whole thing every time. You could add a simple function in your module that only takes an ID and returns the tags:
There is however a workaround, which is to use .
Objects and Arrays are a little more complex to deal with, so they have their own page. See for more information.
That's pretty much it for only retrieving a single data value. There are more complex operations that are available, take a look at for the more advanced things you can do on Enmap's data!
The third parameter in push is the "path" to the array in an object. It works the same as the properties path used in .
To answer my own obvious question: it's not a bug, it's a feature that I cannot implement. The way Enmap's cache works is that the data is loaded in memory _in that _of Enmap, and only for that instance. This is what enables you to have many different Enmaps in your project - one Enmap doesn't share data with another.
Important Note: Do NOT override Discord.js' existing collections! That means, client.users, client.guilds, etc. - none of these should be overridden.
All things considered, are probably the recommended way to use your Enmap in multiple files within your project. Not only does it give you a single file to import, lets you define multiple Enmaps you can individually import, it also gives you the ability to add specific functions to do common actions you use throughout your project.
As covered in , modules are fairly straightforward. This is how I have done an Enmap shared module before:
And there you have it! There are other ways to build the exports, you can also split it differently, take a look at for more information.
Enmap is a great way to store structured data, and offers a few helper features that directly affect both objects and arrays.
Let's assume for a moment that we want to store the following data structure in Enmap:
This structure has 5 "properties": first
, second
, changeme
, isCool
, sub
. The sub
property has 2 properties of its own, yay
and thing
.
To store this structure in Enmap, you can use a variable, or just straight-up write the object:
Note: All further methods require the value to be an object. If you attempt to get, set, modify or remove using the below methods and your value isn't an object, Enmap will throw an error.
Retrieving a specific property from an object is done through the get()
method, by specifying both the key and the "path" to the property you want.
The exact method is <Enmap>.get(key, path)
.
You can also check if a specific property exists or not. This is done through the has
method, with a key, and path to the property:
There are a few various ways to modify properties of both Objects and Arrays. The very basic way to set a property on an object or array is through .set(key, value, path)
like the following examples:
As you can see, setProp() and getProp() work on the same concept that the path can be as complex as you want.
Arrays have additional helper methods, you can see them here.
This page is a work in progress and may not have the polish of a usual Evie-Written document!
Some quick docs:
Possible Operators (accepts all variations listed below, as strings):
+
, add
, addition
: Increments the value in the enmap by the provided value.
-
, sub
, subtract
: Decrements the value in the enmap by the provided value.
*
, mult
, multiply
: Multiply the value in the enmap by the provided value.
/
, div
, divide
: Divide the value in the enmap by the provided value.
%
, mod
, modulo
: Gets the modulo of the value in the enmap by the provided value.
^
, exp
, exponential
: Raises the value in the enmap by the power of the provided value.
As described in the home page, one disadvantage of Enmap is that it loads all your data in memory, so you're sacrificing RAM in order to gain speed. In larger projects, this might become a concern fairly quickly - or when using larger data sets that take more memory.
For this purpose, there are features in Enmap that enable less caching, by sacrificing some speed and ease of use. That is to say, with data not being fully loaded, there are some things that can't be done easily - see below for details.
The options are as follow:
fetchAll
: Defaults to true
, which means fetching all keys on load. Setting it to false
means that no keys are fetched, so it loads faster and uses less memory.
autoFetch
: Defaults to true
. When enabled, will automatically fetch any key that's requested using get, getProp, etc. This is a "synchronous" operation, which means it doesn't need any of this promise or callback use.
If fetchAll is set to false, no data (by default) will be loaded from the database - the Enmap will be completely empty. This means that doing a .size
check returns 0, looping and filtering doesn't return anything, and get() requests all return null.
Ok but... how's that useful? It's useful because if you don't need the data, it's not loaded. To load data, there are 2 different methods available.
enmap.fetchEverything()
will, of course, fetch all the data from the database - this is the method that's called on init() if fetchAll is true. This means you can stagger your loading time, or that you can decide, later, under certain conditions, you do want to load everything.
enmap.fetch(key)
can fetch a single key from the database, saves it to enmap, and returns a promise with the fetched value.
enmap.fetch([array, of, keys])
will fetch each key in the requested array, and return an array of [key, value]
pairs for each fetched value.
enmap.count
will give you the number of keys in the database itself, including uncached ones (.size is only cached values).
enmap.indexes
will give you a list of keys in the database, including uncached ones.
Yup. Those are the only things you really need to know for the current version of Enmap's fetchAll feature.
This page describes using 2 different enmaps in tandem using autonum and a reference array.
An interesting problem in javascript is that having an array of objects can be quite the ordeal. A lot of things you want to do require functions and loops, bleh. So, where Enmap is meant to be easier to use, this is an area where it's still a bit hard to handle things.
But there's a solution. If Enmap isn't enough, how about **TWO ** Enmap???? So yeah, we're going to be using one enmap to store user data, and another to store "warnings", that is to say, moderation actions stored as objects.
When we add a new element to the actions enmap, we'll be adding a reference to that new entry in the user enmap, via the autonum
feature.
If you've read about databases a bit, you might have heard about "autonum" and "automatic indexes" before, and this is exactly it. Alright let's get down to brass tax!
The actions enmap will be using autonum to generate new unique numbers that we'll be able to reference later. The setup is very typical, to begin. We'll attach these things to the discord.js client to keep things simple.
When we want to create a new action, it's a simple act of using autonum to get a key automatically. Let's do a simple warning:
So what this does is twofold: it gives us an ID, as well as save the data for this new warning in the Enmap.
You might already have one of those enmaps lying around, but if you don't, the deal's pretty much the same (because enmap is simple!):
We of course need to have some properties in there, and this will be done using ensure(). This is very similar to our Points system, and it can be done on user join (guildMemberAdd) and/or in the message event. Both would be fine:
So now we have everything ready to create a simple warn command that will use the above setup to create what we need:
So how does this help us in the end? If you look at the warnings, you only get a bunch of IDs, right? Well, we can most definitely do some array magic in order to get these proper values... Yeah let's do that. Abracadabra!
Now go have fun an explore the endless possibilities of this system :D
From the first moment where I started using the Discord.js library, one thing in it fascinated me: "Collections". Discord.js Collections are a Map structure from JavaScript on top of which a bunch of useful methods are added, most notably those from JavaScript's Array structure.
Things like map, filter, reduce, find, sort... they made Maps so useful, so much more powerful, that I admired their design. It struck me at one point, that if such a structure were to be separated from Discord.js and perhaps made to be saved in a database, it would make interacting with data so easy that even a child could do it.
So when I started getting seriously into bot that required their own data to be saved, I turned to Amish Shah (Hydrabolt) and I said to him, I said "Listen, buddy, can I extract Collections and publish them as a separate module? That'd be awesome!" and Amish replied, like the great guy he is, "uhhhh sure, why not?"
And so, in May 2017, the djs-collection
module was born. It was a simple thing, just straight-up lifted from Discord.js' code (not illegally, mind you, I retained all proper licenses and credits to Hydrabolt!). The following month, I added persistence (saving to a database) to it and published djs-collection-persistent
, which then became my own defacto way to save data to a database.
But... let's be honest, npm install --save djs-collection-persistent
is a mouthful to type out. Plus, I was realizing that having those two as separate modules meant I had to update them separately and ensure they still worked individually... So at one point, I decided it was time to merge them.
Releasing a single repository meant that I could now change the name, because of the aformentioned "omg mile-long name" problem, and just the overall annoyance of writing it. So I settled on "well, they're enhanced maps, let's call it Enmap!". A quick search revealed Enmap's only other apparent meaning was that it was the name of a satellite, and I was guessing no one would confuse the two.
But I didn't want to force enmap users to have persistence, so at the same time I actually created a separate module called enmap-level
, which controlled the database layer and was completely optional. These modules I called Providers since, obviously, they provided data persistence and and API.
Enmap 0.4.0 was released at the beginning of October 2017, and since then has grown into a fairly solid module used by tens of thousands of people across the world, not only in discord.js bots but also in other projects built with Node. Its solidity and simplicity makes it the ideal storage for simple key/value pairs, and I'm extremely proud to have made it.
At the moment of writing this (2018-09-02) Enmap has been downloaded over 32,000 times and is growing by the minute with almost 10,000 downloads in August alone!
It's been a year now since I last wrote this post. And in case you were wondering, growth hasn't stopped! In fact, it's quite accelerated. One big change that I made was to go back to a single locked-in database provider, which I describe in Why SQLite Only?
Other than that, and adding some new features due to the switch to better-sqlite3, the main event is that just las month I reached a whopping 500,000 downloads for Enmap. Yes, that's a half million downloads for my little useful module that I started making for myself and ended up being useful for so many.
In this example we'll be using Enmap to store user data in order to authenticate users on a simple Koa application. In order to make this secure, we'll be using bcrypt
to encrypt the passwords, so of course they will not be plain text in the database.
We'll be using koa
as a web server module, along with koa-session
in order to store the session information. Additionally, for ease of use, koa-router
is used to simplify the route code.
This tutorial uses
koa-session
which, by default, is insecure since it stores the entire session data in a browser cookie. This means the password, though encrypted, would be availble in the cookie, and easy to spoof. There are many session stores available for different storage system, but using them is beyond the scope of this example.
To install those requirements, run the following in a new, empty folder for your project:
Once all of those are installed, we're ready to start! We're going to create an index page, login page, and one "protected" page that requires login. Let's start with the top of the file, which is all the required modules:
So now we have all the basics necessary.
Let's create a few functions specifically for the login features, related to enmap. First, a function to create a new user:
This function takes in the following arguments:
username
which obviously is self-explanatory: it's the username entered during login.
name
is the "full name" or "display name", for a friendly display on the page or an email.
plainpw
is the password desired for this account. It has to come in as plain text, obviously, in order to be properly saved in the database.
admin
is a boolean value representing whether the user should be administrator. If creating something like a blog, only an administrator could create other administrators. It's false by default.
The function first checks if the username exists and returns an error if it does. It then generates a salted, hashed version of the password which it stores in the database. Don't let the name fool you, the password is not "encrypted", which implies that it can be decrypted. Instead, it's a "cryptographic hash functions", a unidirectional function that cannot be undone. The only way to verify that a password is correct is to re-hash it again and compared the hashes.
Once the hashed password is obtained, the user itself is stored in the database with all 4 incoming arguments except the password which is the hashed version.
The login function takes in the username and the incoming plain password and verifies that the hashed version corresponds with the one stored in the database.
An important point here is that this function returns a promise in all cases. If the username doesn't exist or the password is blank, a false
response is returned in a promise. Otherwise, the response of bcrypt's compare
function is returned. This function returns true if the passwords match, false if they do not.
There's a few configuration items we need to take care of. First off, the session settings:
Then we need to setup how Koa will handle rendering EJS pages. This is one pretty awesome thing about Koa, that this can be setup automatically and globally, but don't let me gush all over this!
So let's establish our "routes", which is the pages that can be accessed by the browser. With the help of the Router, this can be really straightforward.
Then we have the login route, which does a lot of the bulk of our work. It checks for login, and adds everything it needs to the session in Koa if the authentication is successful:
Let's also create a logout function, that simply destroys the current session and returns the user to the index:
This one is pretty straightforward, so I don't think I need to get into the details, right? ;)
Lastly, we have the route for our "private" page. the one that only works if you're logged in. Now, there are "better" ways to establish protected routes, but let's go with the simplest one for now. We're just going to check for the logged
property of the session to determine if the user is logged in.
At the very end of our file we still have a bit of stuff to add. Mainly starting the server, but also telling the parser and routers to initialize. This would be how it's done:
While templating is slightly beyond the scope of an authentication tutorial, I would be remiss to ignore the fact that logging in without a page would be... let's say a little hard.
Koa's EJS templating configuration, that we did above, means that templates need to appear in the views
folder. There will be a few template files:
template.html
will be the "main" template. It will have the header, footer, and whatever else we want to appear on every page.
index.html
will be the main page everyone can access.
login.html
contains the login page and form
secret.html
has a little secret about something.
Let's start with the template.html
file.
The <%- body %>
tag is where the contents of the other pages appear.
The index.html
is just some welcome thingy that we aren't concerned about:
Then we have the login.html
page which has our form. The form simply posts back to itself, so it'll trigger the .post
endpoint:
And, finally, the secret.html
page we've all been waiting for. Nothing that you haven't heard before, though:
Our first task is of course to initialize the enmap correctly. In this case, we're attaching the settings to our client object so we can use it in different commands.
The main event for our bot, where messages are received. Any error here will probably crash the bot on every message received, so be careful!
Learn how to manipulate the data you save and retrieve from the database, to more easily store complex data without having to convert it to simple data everywhere you use it.
Introduced in Enmap 5.6, Serializers and Deserializers are functions that you use to manipulate the data before storing it in the database, or before using it after retrieving it.
This feature is born from a limitation in Enmap: it cannot store very complex objects, such as the instance of a class, objects with circular references, functions, etc. So, typically when you have such data, you need to manually convert it to some simple representation before storing, and then do the inverse after getting it from enmap. This is a more automated way of doing it.
The Serializer function runs every single time data is stored in the enmap, if one is provided. This function receives the data provided to set() as an input, and must return a value to be stored in the database. This function MUST be synchronous, that is to say, cannot be an async function or return a promise.
The Deserializer function is the reverse, and runs on each value pulled from the database, before it is returned through the get() method. This function receives the data stored in the database and returns the value that you want to use directly. This function MUST be synchronous, that is to say, cannot be an async function or return a promise.
Taking a hit from my own example of Per-Server Settings, this is a better example that doesn't require storing just the name of a channel, but straight-up the channel itself.
This page explains the reason behind the removal of the "Provider" system, and the selection of sqlite as the only database available for Enmap starting version 4
So one of the major changes from Enmap 3 to 4 is the removal of Providers. Providers were something I've had since Enmap 1.0 (when I converted from djs-collections-persistent), and had 2 advantages (2 reasons to have them in the first place).
It enabled supporting more databases, not only one. This gave more power to users, and, I thought, more capabilities.
It separated the memory enmap (non-persistent) from the database layer, so installing enmap didn't require installing sqlite.
But, after a year of updating Enmap, I realized that I'd painted myself in a corner with Providers. There came to light that there were multiple limitations to providers:
Features were limited to the "lowest common denominator", whatever was available to all providers. For instance, better-sqlite3 is a synchronous module that's nonblocking (which is a magical thing, really). But since all other providers required promises, then I had to use sqlite as a promise module.
Maintaining multiple providers is hard work. Every new feature would require updating all the providers (5 at this time), and there were many requests to create new providers which is an annoying, sometimes complicated task that adds even more work in the future.
In addition, the advantages became lesser with time. I realized most people were using leveldb at first, then most switch to sqlite when I updated guides to use that provider. Essentially, most people use whatever they're told to use. So, just forcing one database wasn't that much of an issue and didn't affect the majority of users.
Also, most people did use enmap with persistence, and those that didn't... well, most users have enmap to use with discord.js bots in the first place which gives them Collection - almost the same as a non-persistent enmap.
The reasoning behind removing all other providers and keeping sqlite was for specific features and capabilities inherent to the module I'm using, better-sqlite3.
better-sqlite3 is, as I mention, synchronous , which means, no callbacks, no promises. Just straight-up "make a request and it does it before the next line". No more need for "waiting" for things, resolving promises, etc.
The sync nature of better-sqlite3 means I can add an autoFetch feature. I can simply say "If the key isn't there, try to get the data", without requiring the user to resolve a promise. This is awesome.
By the same token, I can also add simple things like "get all keys in the database" using a getter. This means you can do enmap.indexes
and this is actually querying the database seamlessly without the user really knowing it does that. Same for enmap.count
and other features I'm planning.
So overall, I'm happy with my decision. It gives me more power, it gives users more features, and the people affected by the removal of the other providers are few and far between. Hell, enmap-pgsql has less than 1000 downloads on npm which is mostly mirrors and caches. It showed me that provider was pretty useless in the first place.
I recognize that some people might want to use enmap and can't use sqlite. This is for many valid reasons, for example using it on heroku which doesn't support sqlite and leveldb. For those users, I'm keeping the providers open for maintenance. If someone wants to maintain and update the V3 branch, or even fork the entire system and maintain it under a new name, I have no issue with that (assuming licenses are properly kept). I'll accept PRs on all enmap repositories, including backporting some features and adding new ones.
I'm also keeping the V3 docs in this gitbook so it can be maintained through gitbook and PRed on github.
You can still install any provider as you would before, and install enmap using npm i eslachance/enmap#v3
for the version 3 branch that will remain.
Want to help out? Join the Discord, fork and PR the Github for enmap, contribute!
This points bot is simple, but functional. Make sure you've followed the Installation Instructions before doing the following.
First, you need to create a new persistent Enmap. Here's how it goes:
That will create a new Enmap under the name of points, and attaches it to the client object so it can be used where ever you have access to the client object.
The obvious goal of a points system is to accumulate fake internet points and gloat about it. So, of course, that's going to be our first focus. In this example implementation, we will make the points guild-specific, and unusable in DMs. Points will still accumulate even if the user does a command, which simplifies our code a bit.
We do have a small caveat - we really don't want to react on Direct Messages, so our whole code will be in a block that checks for that:
Our very first step is going to be to initialize a new entry in the enmap for any new user - one we haven't received a message from before. This is done using the enmap.ensure(key, defaultvalue)
method, which can check if a specific key exists in the Enmap, write it if it doesn't (and return the defaultvalue in this case). Note that our keys take the format of guildid-userid
so they're unique to the guild and the user. Also, our data in this case is a complete object, which we'll take advantage of fully.
There's obviously a few ways we could have done this, including some fancy ternary condition or whatever. I will, however, keep this code as simple to read as possible.
The following bit is super simple - Enmap has a method to directly increment a value in Enmap even if it's in an object. Pretty clever if I do say so myself!
Have your own way of incrementing points? No problem! Enmap.math() gives you the ability to add, multiply, and act upon any numerical value or property. To add 10 points, for instance, client.points.math(key, "+", 10, "points")
would be used.
Time to level up! If a user has enough points, they will go up a level. Now we have to do some math here, but don't run off in fear, this one's pretty easy. This is how we calculate the levels:
This line will calculate the square root of currentPoints
then multiplies that result by 0.1 then floors that result for a round number.
Now we should work out if you've amassed enough points to actually level up, by grabbing the current user's level and comparing them. If the new calculated level is higher, it means the user leveled up and we can deal with that, first by sending them a very annoying mee6-inspired message!
Lastly, we want to update the score.level
value with the new level so throw this under the message.reply
.
So here's the whole thing from top to bottom, with bonus comments!
Alright, that's the bulk of the code, you could throw this into your bot and it would work like a charm, however your users wouldn't know how many points, or even their levels, so let's fix that, make a new command called points
, which will also show them their level.
Obviously there's no way for us to know how you're making commands, so again we'll assume you're doing a bot in a single js file. You may need to adjust the code, of course!
So let's re-iterate our current starting position.
The points
command would look like this:
Let's finish this off with a very simple leaderboard
command that will show the top 10 users in the current guild. For this we'll need to filter the Enmap to only get the users for the current guild, then we'll convert the results to an array, sort that, and keep the first 10 results only.
We convert to an array because an Enmap
, just like its underlying Map
structure, is not ordered and thus cannot be sorted. It may seem ordered because it stores by keys, but that's actually a quirk, not a feature.
So here's our leaderboard command:
Let's take a quick peek at what Josh is, and what it means for the future of Enmap
But since that moment where Providers were removed, I had a plan in mind to give those that needed them an escape route. And not only that, Enmap itself does have some pretty solid limitations when it comes to growth, because of its lack of ability to support multiple processes and sharded applications.
So I've been working on it for a few years now - not full time, mind you, as it would have been ready a long time ago, but as a side project. It's finally picked up steam, and you can Get Josh right now to try out the early access version. It's limited (not as powerful as Enmap is currently) but that's rapidly evolving.
You might immediately wonder, "But Evie, if you're working on Josh, what's going to happen with Enmap?" and I'm telling you right now, you don't need to worry about this. Enmap is still growing in popularity, I still have things to do with it, and I fully intend on maintaining and enhancing it in the future.
Josh might be similar to Enmap but it's not made to replace it! It has a different purpose, which is to support larger applications, potentially web-based ones, provide live updates, and all the things that were lost with Enmap's great provider purge. And since Josh is promise-based, it's not as simple to pick up as Enmap was, so I do expect people to start off with Enmap either way.
It is my sincere hope that Enmap and Josh will both continue to grow, to help more people, and to help us all create better code, together!
The people that help make Enmap, and my other projects, possible!
My home for many years, way before Enmap was even a thought, AIG has been the place where I can be myself. That is, be sometimes of an asshole and sometimes helpful to beginners wanting to learn discord.js. Sometimes both at the same time!
The first supporter on GitHub and Patreon, working towards a degree in IT and an occasional idiot on An Idiots Guide.
He's Andrew()#9999 on Discord, and always willing to tell you if your nose looks weird.
The complete and unadultered API documentation for every single method and property accessible in Enmap.
The following is the complete list of methods available in Enmap. As it is auto-generated from the source code and its comments, it's a little more "raw" than the Usage docs. However, it has the benefit of being more complete and usually more up to date than the manually written docs.
If you're doing a PR on the docs github, please do not manually edit the below contents, as it will be overwritten. Check the src/index.js source code and change the comments there instead!
Initializes a new Enmap, with options.
Example
integer
Retrieves the number of rows in the database for this enmap, even if they aren't fetched.
array.<string>
Retrieves all the indexes (keys) in the database for this enmap, even if they aren't fetched.
number
Generates an automatic numerical key for inserting a new value. This is a "weak" method, it ensures the value isn't duplicated, but does not guarantee it's sequential (if a value is deleted, another can take its place). Useful for logging, actions, items, etc - anything that doesn't already have a unique ID.
Sets a value in Enmap.
Example
*
Update an existing object value in Enmap by merging new keys. This only works on objects, any other value will throw an error. Heavily inspired by setState from React's class components. This is very useful if you have many different values to update and don't want to have more than one .set(key, value, prop) lines.
Example
*
Retrieves a key from the enmap. If fetchAll is false, returns a promise.
Example
*
Returns an observable object. Modifying this object or any of its properties/indexes/children will automatically save those changes into enmap. This only works on objects and arrays, not "basic" values like strings or integers.
Fetches every key from the persistent enmap and loads them into the current enmap value.
Force fetch one or more key values from the enmap. If the database has changed, that new value is used.
Removes a key or keys from the cache - useful when disabling autoFetch.
Function called whenever data changes within Enmap after the initial load. Can be used to detect if another part of your code changed a value in enmap and react on it.
Example
Promise.<*>
Shuts down the database. WARNING: USING THIS MAKES THE ENMAP UNUSEABLE. You should only use this method if you are closing your entire application. This is useful if you need to copy the database somewhere else, or if you're somehow losing data on shutdown.
Push to an array value in Enmap.
Example
Executes a mathematical operation on a value and saves it in the enmap.
Example
Increments a key's value or property by 1. Value must be a number, or a path to a number.
Example
Decrements a key's value or property by 1. Value must be a number, or a path to a number.
Example
*
Returns the key's value, or the default given, ensuring that the data is there. This is a shortcut to "if enmap doesn't have key, set it, then get it" which is a very common pattern.
Example
boolean
Returns whether or not the key exists in the Enmap.
Example
boolean
Deletes a key in the Enmap.
Deletes everything from the enmap. If persistent, clears the database of all its data for this table.
null
Completely destroys the entire enmap. This deletes the database tables entirely. It will not affect other enmap data in the same database, however. THIS ACTION WILL DESTROY YOUR DATA AND CANNOT BE UNDONE.
Remove a value in an Array or Object element in Enmap. Note that this only works for values, not keys. Note that only one value is removed, no more. Arrays of objects must use a function to remove, as full object matching is not supported.
Example
string
Exports the enmap data to a JSON file. WARNING: Does not work on memory enmaps containing complex data!
Import an existing json export from enmap from a string. This data must have been exported from enmap, and must be from a version that's equivalent or lower than where you're importing it.
Array
Creates an ordered array of the values of this Enmap. The array will only be reconstructed if an item is added to or removed from the Enmap, or if you change the length of the array itself. If you don't want this caching behaviour, use Array.from(enmap.values())
instead.
Array.<(string|number)>
Creates an ordered array of the keys of this Enmap The array will only be reconstructed if an item is added to or removed from the Enmap, or if you change the length of the array itself. If you don't want this caching behaviour, use Array.from(enmap.keys())
instead.
*
| Array.<*>
*
| Array.<*>
Array
Searches for all items where their specified property's value is identical to the given value (item[prop] === value
).
Example
*
All Enmap used in Discord.js are mapped using their `id` property, and if you want to find by id you should use the `get` method. See [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) for details.
Example
Example
string
| number
Example
Example
number
Removes entries that satisfy the provided filter function.
Array
Array
boolean
boolean
*
Creates an identical shallow copy of this Enmap.
Combines this Enmap with others into a new Enmap. None of the source Enmaps are modified.
Example
Array.<Map>
Initialize multiple Enmaps easily.
Example
With all said and done, . Want a more "advanced" version of this project? Check out .
This example uses a very, very simple bot made in discord.js to demonstrate how easily can be used to create a per-server configuration system.
Remember to follow the before running any of this!
There were features I wanted that simply weren't possible, physically, with the providers ().
Our starting point is a very basic message handler with pre-existing commands - such as what we see in the page on An Idiot's Guide. The code is as such:
As I've noted in my, when Enmap moved to SQLite only, there were a few feathers and features lost in transition. Most notably, the loss of Providers was a big one, even though in my opinion it was a good trade-off to get the new features I wanted to include in Enmap 4 and onward.
Then plan was , all along. Josh is the Javascript Object Storage Helper, and if that sounds a lot like what Enmap does it's because it is. In fact, could best be described to you, my reader, as "A version of Enmap that doesn't have caching, is promised-based, and supports providers again".
Josh and Enmap should, and will, be fully compatible with one another, in that you will be able to easily migrate between them (with () and () ), and moving from one to another would require a minimal amount of code changes. It's not zero, but I'm trying as much as possible to keep those differences as small as possible.
I've already back-ported a few things that I originally intended for Josh as part of Enmap's new updates. The () method, as well as the feature, were originally intended for Josh but ended up being implementable in Enmap also. This means, if I add a feature to Josh, I will add it to Enmap if I can, if it's compatible. So you won't be left behind!
AIG helps users with discord.js support for version 11 and 12, has a channel for Enmap, and is a community of builders. Notably, we're the authors of the famous (if that wasn't obvious), where I'm the main author but there have been many contributions over the year. For discord.js support, head on over to for the guide itself and to join us on Discord.
Kind: instance property of
Returns: integer
- The number of rows in the database.
Kind: instance property of
Returns: array.<string>
- Array of all indexes (keys) in the enmap, cached or not.
Kind: instance property of
Returns: number
- The generated key number.
Example
Kind: instance method of Returns: - The enmap.
Kind: instance method of
Returns: *
- The updated, merged value.
Kind: instance method of
Returns: *
- The value for this key.
Kind: instance method of
Returns: *
- The value for this key.
Kind: instance method of Returns: - The enmap containing all values.
Kind: instance method of
Returns: | *
- The Enmap, including the new fetched values, or the value in case the function argument is a single key.
Kind: instance method of Returns: - The enmap minus the evicted keys.
Kind: instance method of
Kind: instance method of
Returns: Promise.<*>
- The promise of the database closing operation.
Kind: instance method of Returns: - The enmap.
Kind: instance method of Returns: - The enmap.
Kind: instance method of Returns: - The enmap.
Kind: instance method of Returns: - The enmap.
Kind: instance method of
Returns: *
- The value from the database for the key, or the default value provided for a new key.
Kind: instance method of
Performs Array.includes() on a certain enmap value. Works similar to .
Kind: instance method of
Returns: boolean
- Whether the array contains the value.
Kind: instance method of Returns: - The enmap.
Kind: instance method of
Kind: instance method of
Kind: instance method of Returns: - The enmap.
Kind: instance method of
Returns: string
- The enmap data in a stringified JSON format.
Kind: instance method of Returns: - The enmap with the new data.
Kind: instance method of
Kind: instance method of
Obtains random value(s) from this Enmap. This relies on .
Kind: instance method of
Returns: *
| Array.<*>
- The single value if count
is undefined, or an array of values of count
length
Obtains random key(s) from this Enmap. This relies on
Kind: instance method of
Returns: *
| Array.<*>
- The single key if count
is undefined, or an array of keys of count
length
Kind: instance method of
Searches for a single item where its specified property's value is identical to the given value (item[prop] === value
), or the given function returns a truthy value. In the latter case, this is identical to .
Kind: instance method of
Searches for the key of a single item where its specified property's value is identical to the given value (item[prop] === value
), or the given function returns a truthy value. In the latter case, this is identical to .
Kind: instance method of
Kind: instance method of
Returns: number
- The number of removed entries
Identical to , but returns a Enmap instead of an Array.
Kind: instance method of
Identical to .
Kind: instance method of
Identical to .
Kind: instance method of
Identical to .
Kind: instance method of
Identical to .
Kind: instance method of
Identical to .
Kind: instance method of
Kind: instance method of Example
Kind: instance method of
Kind: static method of
Returns: Array.<Map>
- An array of initialized Enmaps.
Param
Type
Default
Description
iterable
iterable
| string
If iterable data, only valid in non-persistent enmaps. If this parameter is a string, it is assumed to be the enmap's name, which is a shorthand for adding a name in the options and making the enmap persistent.
[options]
Object
Additional options for the enmap. See https://enmap.evie.codes/usage#enmap-options for details.
[options.name]
string
The name of the enmap. Represents its table name in sqlite. If present, the enmap is persistent. If no name is given, the enmap is memory-only and is not saved in the database. As a shorthand, you may use a string for the name instead of the options (see example).
[options.fetchAll]
boolean
Defaults to true
. When enabled, will automatically fetch any key that's requested using get, or other retrieval methods. This is a "syncroneous" operation, which means it doesn't need any of this promise or callback use.
[options.dataDir]
string
Defaults to ./data
. Determines where the sqlite files will be stored. Can be relative (to your project root) or absolute on the disk. Windows users , remember to escape your backslashes!
[options.cloneLevel]
string
Defaults to deep. Determines how objects and arrays are treated when inserting and retrieving from the database. See https://enmap.evie.codes/usage#enmap-options for more details on this option.
[options.ensureProps]
boolean
defaults to true
. If enabled and the value in the enmap is an object, using ensure() will also ensure that every property present in the default object will be added to the value, if it's absent. See ensure API reference for more information.
[options.autoEnsure]
*
default is disabled. When provided a value, essentially runs ensure(key, autoEnsure) automatically so you don't have to. This is especially useful on get(), but will also apply on set(), and any array and object methods that interact with the database.
[options.autoFetch]
boolean
defaults to true
. When enabled, attempting to get() a key or do any operation on existing keys (such as array push, etc) will automatically fetch the current key value from the database. Keys that are automatically fetched remain in memory and are not cleared.
[options.serializer]
function
Optional. If a function is provided, it will execute on the data when it is written to the database. This is generally used to convert the value into a format that can be saved in the database, such as converting a complete class instance to just its ID. This function may return the value to be saved, or a promise that resolves to that value (in other words, can be an async function).
[options.deserializer]
function
Optional. If a function is provided, it will execute on the data when it is read from the database. This is generally used to convert the value from a stored ID into a more complex object. This function may return a value, or a promise that resolves to that value (in other words, can be an async function).
[options.wal]
boolean
false
Check out Write-Ahead Logging: https://www.sqlite.org/wal.html
Param
Type
Default
Description
key
string
Required. The key of the element to add to The Enmap.
val
*
Required. The value of the element to add to The Enmap. If the Enmap is persistent this value MUST be stringifiable as JSON.
path
string
null
Optional. The path to the property to modify inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Description
key
string
The key of the object to update.
valueOrFunction
*
Either an object to merge with the existing value, or a function that provides the existing object and expects a new object as a return value. In the case of a straight value, the merge is recursive and will add any missing level. If using a function, it is your responsibility to merge the objects together correctly.
Param
Type
Default
Description
key
string
The key to retrieve from the enmap.
path
string
null
Optional. The property to retrieve from the object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Default
Description
key
*
The key to retrieve from the enmap.
path
string
null
Optional. The property to retrieve from the object or array.
Param
Type
Description
keyOrKeys
string
| number
| Array.<(string|number)>
A single key or array of keys to force fetch from the enmap database.
Param
Type
Description
keyOrArrayOfKeys
string
| number
| Array.<(string|number)>
A single key or array of keys to remove from the cache.
Param
Type
Description
cb
function
A callback function that will be called whenever data changes in the enmap.
Param
Type
Default
Description
key
string
Required. The key of the array element to push to in Enmap. This value MUST be a string or number.
val
*
Required. The value to push to the array.
path
string
null
Optional. The path to the property to modify inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
allowDupes
boolean
false
Optional. Allow duplicate values in the array (default: false).
Param
Type
Default
Description
key
string
The enmap key on which to execute the math operation.
operation
string
Which mathematical operation to execute. Supports most math ops: =, -, *, /, %, ^, and english spelling of those operations.
operand
number
The right operand of the operation.
path
string
null
Optional. The property path to execute the operation on, if the value is an object or array.
Param
Type
Default
Description
key
string
The enmap key where the value to increment is stored.
path
string
null
Optional. The property path to increment, if the value is an object or array.
Param
Type
Default
Description
key
string
The enmap key where the value to decrement is stored.
path
string
null
Optional. The property path to decrement, if the value is an object or array.
Param
Type
Default
Description
key
string
Required. The key you want to make sure exists.
defaultValue
*
Required. The value you want to save in the database and return as default.
path
string
null
Optional. If presents, ensures both the key exists as an object, and the full path exists. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Default
Description
key
string
Required. The key of the element to add to The Enmap or array. This value MUST be a string or number.
path
string
null
Optional. The property to verify inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Default
Description
key
string
Required. The key of the array to check the value of.
val
string
| number
Required. The value to check whether it's in the array.
path
*
Required. The property to access the array inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Default
Description
key
string
Required. The key of the element to delete from The Enmap.
path
string
null
Optional. The name of the property to remove from the object. Can be a path with dot notation, such as "prop1.subprop2.subprop3"
Param
Type
Default
Description
key
string
Required. The key of the element to remove from in Enmap. This value MUST be a string or number.
val
*
| function
Required. The value to remove from the array or object. OR a function to match an object. If using a function, the function provides the object value and must return a boolean that's true for the object you want to remove.
path
string
null
Optional. The name of the array property to remove from. Can be a path with dot notation, such as "prop1.subprop2.subprop3". If not presents, removes directly from the value.
Param
Type
Default
Description
data
string
The data to import to Enmap. Must contain all the required fields provided by export()
overwrite
boolean
true
Defaults to true
. Whether to overwrite existing key/value data with incoming imported data
clear
boolean
false
Defaults to false
. Whether to clear the enmap of all data before importing (WARNING: Any exiting data will be lost! This cannot be undone.)
Param
Type
Description
[count]
number
Number of values to obtain randomly
Param
Type
Description
[count]
number
Number of keys to obtain randomly
Param
Type
Description
prop
string
The property to test against
value
*
The expected value
Param
Type
Description
propOrFn
string
| function
The property to test against, or the function to test with
[value]
*
The expected value - only applicable and required if using a property for the first argument
Param
Type
Description
propOrFn
string
| function
The property to test against, or the function to test with
[value]
*
The expected value - only applicable and required if using a property for the first argument
Param
Type
Description
fn
function
Function used to test (should return a boolean)
[thisArg]
Object
Value to use as this
when executing function
Param
Type
Description
fn
function
Function used to test (should return a boolean)
[thisArg]
Object
Value to use as this
when executing function
Param
Type
Description
fn
function
Function used to test (should return a boolean)
[thisArg]
Object
Value to use as this
when executing function
Param
Type
Description
fn
function
Function that produces an element of the new array, taking three arguments
[thisArg]
*
Value to use as this
when executing function
Param
Type
Description
fn
function
Function used to test (should return a boolean)
[thisArg]
Object
Value to use as this
when executing function
Param
Type
Description
fn
function
Function used to test (should return a boolean)
[thisArg]
Object
Value to use as this
when executing function
Param
Type
Description
fn
function
Function used to reduce, taking four arguments; accumulator
, currentValue
, currentKey
, and enmap
[initialValue]
*
Starting value for the accumulator
Param
Type
Description
...enmaps
Enmaps to merge
Param
Type
Description
names
Array.<string>
Array of strings. Each array entry will create a separate enmap with that name.
options
Object
Options object to pass to each enmap, excluding the name..