diff --git a/.eslintrc.json b/.eslintrc.json
index 30da694..d7924bc 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -8,7 +8,8 @@
"es6": true
},
"parserOptions": {
- "ecmaVersion": 2022
+ "ecmaVersion": 2023,
+ "sourceType": "module"
},
"rules": {
"no-unused-vars": 1,
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a33dba0..d741323 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,5 +21,4 @@ jobs:
run: npm install
- name: Run Tests
run: npm run test
- - name: Build docs
- run: npm run docs
+
diff --git a/.gitignore b/.gitignore
index 96c0995..e69c781 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,5 @@ out/
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
-!.yarn/versions
\ No newline at end of file
+!.yarn/versions
+.retype/
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..a968fc9
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,36 @@
+---
+description: >-
+ 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!
+---
+
+# What is Enmap?
+
+
+
+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.
+
+{% hint style="danger" %}
+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.
+{% endhint %}
+
+## Why Enmap?
+
+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.
+
+## Advantage/Disadvantage
+
+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](install/).
+* **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.
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
new file mode 100644
index 0000000..b8dad17
--- /dev/null
+++ b/docs/SUMMARY.md
@@ -0,0 +1,22 @@
+# Table of contents
+
+* [What is Enmap?](README.md)
+* [Enmap Installation](install/README.md)
+ * [Troubleshooting Guide](install/troubleshooting-guide.md)
+ * [Migrating data from Enmap 3](install/upgrade.md)
+ * [Upgrading to Enmap 6](install/upgradev6.md)
+* [Usage Documentation](usage/README.md)
+ * [Basic Data Use](usage/basic.md)
+ * [Understanding Paths](usage/paths.md)
+ * [Working with Objects](usage/objects.md)
+ * [Array Methods](usage/arrays.md)
+ * [Mathematical Methods](usage/math.md)
+ * [Using from multiple files](usage/using-from-multiple-files.md)
+ * [Serializing and Deserializing](usage/serialize.md)
+* [Full Documentation](api.md)
+* [Blog Posts](blog-posts/README.md)
+ * [Enmap's History](blog-posts/enmaps-history.md)
+ * [V4: Why SQLITE only?](blog-posts/why-sqlite-only.md)
+ * [V6: Why remove cache?](blog-posts/why-remove-cache.md)
+ * [Enmap and Josh](blog-posts/josh.md)
+
diff --git a/docs/api-docs.md b/docs/api-docs.md
deleted file mode 100644
index 34095cc..0000000
--- a/docs/api-docs.md
+++ /dev/null
@@ -1,939 +0,0 @@
-
-
-## Enmap ⇐ Map
-A enhanced Map structure with additional utility methods.
-Can be made persistent
-
-**Kind**: global class
-**Extends**: Map
-
-* [Enmap](#enmap-map) ⇐ Map
- * [new Enmap(iterable, [options])](#new-enmap-iterable-options)
- * _instance_
- * [.count](#enmap-count-number) ⇒ number
- * [.indexes](#enmap-indexes-array-less-than-string-greater-than) ⇒ Array.<string>
- * [.db](#enmap-db-database) ⇒ Database
- * [.autonum](#enmap-autonum-number) ⇒ number
- * [.set(key, val, path)](#enmap-set-key-val-path-enmap) ⇒ [Enmap
]
- * [.update(key, valueOrFunction)](#enmap-update-key-valueorfunction)
- * [.get(key, path)](#enmap-get-key-path) ⇒ \*
- * [.observe(key, path)](#enmap-observe-key-path) ⇒ \*
- * [.fetchEverything()](#enmap-fetcheverything-enmap) ⇒ [Enmap
]
- * [.fetch(keyOrKeys)](#enmap-fetch-keyorkeys-enmap-enmap-or) ⇒ [Enmap
](#enmap-map) \| \*
- * [.evict(keyOrArrayOfKeys)](#enmap-evict-keyorarrayofkeys-enmap) ⇒ [Enmap
]
- * [.changed(cb)](#enmap-changed-cb)
- * [.close()](#enmap-close-enmap) ⇒ [Enmap
]
- * [.push(key, val, path, allowDupes)](#enmap-push-key-val-path-allowdupes-enmap) ⇒ [Enmap
]
- * [.math(key, operation, operand, path)](#enmap-math-key-operation-operand-path-enmap) ⇒ [Enmap
]
- * [.inc(key, path)](#enmap-inc-key-path-enmap) ⇒ [Enmap
]
- * [.dec(key, path)](#enmap-dec-key-path-enmap) ⇒ [Enmap
]
- * [.ensure(key, defaultValue, path)](#enmap-ensure-key-defaultvalue-path) ⇒ \*
- * [.has(key, path)](#enmap-has-key-path-boolean) ⇒ boolean
- * [.includes(key, val, path)](#enmap-includes-key-val-path-boolean) ⇒ boolean
- * [.delete(key, path)](#enmap-delete-key-path-enmap) ⇒ [Enmap
]
- * [.deleteAll()](#enmap-deleteall)
- * [.clear()](#enmap-clear-void) ⇒ void
- * [.destroy()](#enmap-destroy-null) ⇒ null
- * [.remove(key, val, path)](#enmap-remove-key-val-path-enmap) ⇒ [Enmap
]
- * [.export()](#enmap-export-string) ⇒ string
- * [.import(data, overwrite, clear)](#enmap-import-data-overwrite-clear-enmap) ⇒ [Enmap
]
- * [.array()](#enmap-array-array) ⇒ Array
- * [.keyArray()](#enmap-keyarray-array-less-than-string-or-number-greater-than) ⇒ Array.<(string\|number)>
- * [.random([count])](#enmap-random-count-or-array-less-than-greater-than) ⇒ \*
\| Array.<\*>
- * [.randomKey([count])](#enmap-randomkey-count-or-array-less-than-greater-than) ⇒ \*
\| Array.<\*>
- * [.findAll(prop, value)](#enmap-findall-prop-value-array) ⇒ Array
- * [.find(propOrFn, [value])](#enmap-find-proporfn-value) ⇒ \*
- * [.findKey(propOrFn, [value])](#enmap-findkey-proporfn-value-string-or-number) ⇒ string
\| number
- * [.sweep(fn, [thisArg])](#enmap-sweep-fn-thisarg-number) ⇒ number
- * [.filter(fn, [thisArg])](#enmap-filter-fn-thisarg-enmap) ⇒ [Enmap
]
- * [.filterArray(fn, [thisArg])](#enmap-filterarray-fn-thisarg-array) ⇒ Array
- * [.map(fn, [thisArg])](#enmap-map-fn-thisarg-array) ⇒ Array
- * [.some(fn, [thisArg])](#enmap-some-fn-thisarg-boolean) ⇒ boolean
- * [.every(fn, [thisArg])](#enmap-every-fn-thisarg-boolean) ⇒ boolean
- * [.reduce(fn, [initialValue])](#enmap-reduce-fn-initialvalue) ⇒ \*
- * [.clone()](#enmap-clone-enmap) ⇒ [Enmap
]
- * [.concat(...enmaps)](#enmap-concat-enmaps-enmap) ⇒ [Enmap
]
- * ~~[.partition(fn, [thisArg])](#Enmap+partition) ⇒ [Array.<Enmap>
](#Enmap)~~
- * ~~[.equals(enmap)](#Enmap+equals) ⇒ boolean
~~
- * ~~[.setProp(key, path, val)](#Enmap+setProp) ⇒ [Enmap
](#enmap-map)~~
- * ~~[.pushIn(key, path, val, allowDupes)](#Enmap+pushIn) ⇒ [Enmap
](#enmap-map)~~
- * ~~[.getProp(key, path)](#Enmap+getProp) ⇒ \*
~~
- * ~~[.deleteProp(key, path)](#Enmap+deleteProp)~~
- * ~~[.removeFrom(key, path, val)](#Enmap+removeFrom) ⇒ [Enmap
](#enmap-map)~~
- * ~~[.hasProp(key, path)](#Enmap+hasProp) ⇒ boolean
~~
- * ~~[.exists(prop, value)](#Enmap+exists) ⇒ boolean
~~
- * _static_
- * [.multi(names, options)](#enmap-multi-names-options-object) ⇒ Object
-
-
-
-### new Enmap(iterable, [options])
-Initializes a new Enmap, with options.
-
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| iterable | Iterable
\| string
\| void
| | 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 "synchronous" 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! *Note*: Will not automatically create the folder if set manually, so make sure it exists. |
-| [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.polling] | boolean
| | 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. |
-| [options.pollingInterval] | number
| | 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. |
-| [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 |
-| [options.verbose] | function
| (query) => null
| A function to call with the direct SQL statement being ran by Enmap internally |
-
-**Example**
-```js
-const Enmap = require("enmap");
-// Non-persistent enmap:
-const inMemory = new Enmap();
-
-// Named, Persistent enmap with string option
-const myEnmap = new Enmap("testing");
-
-// Enmap that does not fetch everything, but does so on per-query basis:
-const myEnmap = new Enmap({name: "testing", fetchAll: false});
-
-// Enmap that automatically assigns a default object when getting or setting anything.
-const autoEnmap = new Enmap({name: "settings", autoEnsure: { setting1: false, message: "default message"}})
-```
-
-
-### enmap.count ⇒ number
-Retrieves the number of rows in the database for this enmap, even if they aren't fetched.
-
-**Kind**: instance property of [Enmap
](#enmap-map)
-**Returns**: number
- The number of rows in the database.
-
-
-### enmap.indexes ⇒ Array.<string>
-Retrieves all the indexes (keys) in the database for this enmap, even if they aren't fetched.
-
-**Kind**: instance property of [Enmap
](#enmap-map)
-**Returns**: Array.<string>
- Array of all indexes (keys) in the enmap, cached or not.
-
-
-### enmap.db ⇒ Database
-Get the better-sqlite3 database object. Useful if you want to directly query or interact with the
-underlying SQLite database. Use at your own risk, as errors here might cause loss of data or corruption!
-
-**Kind**: instance property of [Enmap
](#enmap-map)
-
-
-### enmap.autonum ⇒ 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.
-
-**Kind**: instance property of [Enmap
](#enmap-map)
-**Returns**: number
- The generated key number.
-**Example**
-```js
-enmap.set(enmap.autonum, "This is a new value");
-```
-
-
-### enmap.set(key, val, path) ⇒ [Enmap
](#enmap-map)
-Sets a value in Enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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" |
-
-**Example**
-```js
-// Direct Value Examples
-enmap.set('simplevalue', 'this is a string');
-enmap.set('isEnmapGreat', true);
-enmap.set('TheAnswer', 42);
-enmap.set('IhazObjects', { color: 'black', action: 'paint', desire: true });
-enmap.set('ArraysToo', [1, "two", "tree", "foor"])
-
-// Settings Properties
-enmap.set('IhazObjects', 'blue', 'color'); //modified previous object
-enmap.set('ArraysToo', 'three', 2); // changes "tree" to "three" in array.
-```
-
-
-### enmap.update(key, valueOrFunction)
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| 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. |
-
-**Example**
-```js
-// Define an object we're going to update
-enmap.set("obj", { a: 1, b: 2, c: 3 });
-
-// Direct merge
-enmap.update("obj", { d: 4, e: 5 });
-// obj is now { a: 1, b: 2, c: 3, d: 4, e: 5 }
-
-// Functional update
-enmap.update("obj", (previous) => ({
- ...obj,
- f: 6,
- g: 7
-}));
-// this example takes heavy advantage of the spread operators.
-// More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
-```
-
-
-### enmap.get(key, path) ⇒ \*
-Retrieves a key from the enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
- The value for this key.
-
-| 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" |
-
-**Example**
-```js
-const myKeyValue = enmap.get("myKey");
-console.log(myKeyValue);
-
-const someSubValue = enmap.get("anObjectKey", "someprop.someOtherSubProp");
-```
-
-
-### enmap.observe(key, path) ⇒ \*
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
- The value for this key.
-
-| 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. |
-
-
-
-### enmap.fetchEverything() ⇒ [Enmap
](#enmap-map)
-Fetches every key from the persistent enmap and loads them into the current enmap value.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap containing all values.
-
-
-### enmap.fetch(keyOrKeys) ⇒ [Enmap
](#enmap-map) \| \*
-Force fetch one or more key values from the enmap. If the database has changed, that new value is used.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) \| \*
- The Enmap, including the new fetched values, or the value in case the function argument is a single key.
-
-| Param | Type | Description |
-| --- | --- | --- |
-| keyOrKeys | string
\| number
\| Array.<(string\|number)>
| A single key or array of keys to force fetch from the enmap database. |
-
-
-
-### enmap.evict(keyOrArrayOfKeys) ⇒ [Enmap
](#enmap-map)
-Removes a key or keys from the cache - useful when disabling autoFetch.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap minus the evicted keys.
-
-| Param | Type | Description |
-| --- | --- | --- |
-| keyOrArrayOfKeys | string
\| number
\| Array.<(string\|number)>
| A single key or array of keys to remove from the cache. |
-
-
-
-### enmap.changed(cb)
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| cb | function
| A callback function that will be called whenever data changes in the enmap. |
-
-**Example**
-```js
-enmap.changed((keyName, oldValue, newValue) => {
- console.log(`Value of ${keyName} has changed from: \n${oldValue}\nto\n${newValue}`);
-});
-```
-
-
-### enmap.close() ⇒ [Enmap
](#enmap-map)
-Shuts down the database. USING THIS MAKES THE ENMAP UNUSABLE. You should
-only use this method if you are closing your entire application.
-This is already done by Enmap automatically on shutdown unless you disabled it.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-
-### enmap.push(key, val, path, allowDupes) ⇒ [Enmap
](#enmap-map)
-Push to an array value in Enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - 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). |
-
-**Example**
-```js
-// Assuming
-enmap.set("simpleArray", [1, 2, 3, 4]);
-enmap.set("arrayInObject", {sub: [1, 2, 3, 4]});
-
-enmap.push("simpleArray", 5); // adds 5 at the end of the array
-enmap.push("arrayInObject", "five", "sub"); // adds "five" at the end of the sub array
-```
-
-
-### enmap.math(key, operation, operand, path) ⇒ [Enmap
](#enmap-map)
-Executes a mathematical operation on a value and saves it in the enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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. |
-
-**Example**
-```js
-// Assuming
-points.set("number", 42);
-points.set("numberInObject", {sub: { anInt: 5 }});
-
-points.math("number", "/", 2); // 21
-points.math("number", "add", 5); // 26
-points.math("number", "modulo", 3); // 2
-points.math("numberInObject", "+", 10, "sub.anInt");
-```
-
-
-### enmap.inc(key, path) ⇒ [Enmap
](#enmap-map)
-Increments a key's value or property by 1. Value must be a number, or a path to a number.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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. |
-
-**Example**
-```js
-// Assuming
-points.set("number", 42);
-points.set("numberInObject", {sub: { anInt: 5 }});
-
-points.inc("number"); // 43
-points.inc("numberInObject", "sub.anInt"); // {sub: { anInt: 6 }}
-```
-
-
-### enmap.dec(key, path) ⇒ [Enmap
](#enmap-map)
-Decrements a key's value or property by 1. Value must be a number, or a path to a number.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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. |
-
-**Example**
-```js
-// Assuming
-points.set("number", 42);
-points.set("numberInObject", {sub: { anInt: 5 }});
-
-points.dec("number"); // 41
-points.dec("numberInObject", "sub.anInt"); // {sub: { anInt: 4 }}
-```
-
-
-### enmap.ensure(key, defaultValue, path) ⇒ \*
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
- The value from the database for the key, or the default value provided for a new key.
-
-| 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" |
-
-**Example**
-```js
-// Simply ensure the data exists (for using property methods):
-enmap.ensure("mykey", {some: "value", here: "as an example"});
-enmap.has("mykey"); // always returns true
-enmap.get("mykey", "here") // returns "as an example";
-
-// Get the default value back in a variable:
-const settings = mySettings.ensure("1234567890", defaultSettings);
-console.log(settings) // enmap's value for "1234567890" if it exists, otherwise the defaultSettings value.
-```
-
-
-### enmap.has(key, path) ⇒ boolean
-Returns whether or not the key exists in the Enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| 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" |
-
-**Example**
-```js
-if(enmap.has("myKey")) {
- // key is there
-}
-
-if(!enmap.has("myOtherKey", "oneProp.otherProp.SubProp")) return false;
-```
-
-
-### enmap.includes(key, val, path) ⇒ boolean
-Performs Array.includes() on a certain enmap value. Works similar to
-[Array.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: boolean
- Whether the array contains the value.
-
-| 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 | string
| null
| Optional. The property to access the array inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-
-
-
-### enmap.delete(key, path) ⇒ [Enmap
](#enmap-map)
-Deletes a key in the Enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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" |
-
-
-
-### enmap.deleteAll()
-Deletes everything from the enmap. If persistent, clears the database of all its data for this table.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-
-### enmap.clear() ⇒ void
-Deletes everything from the enmap. If persistent, clears the database of all its data for this table.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-
-### enmap.destroy() ⇒ 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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-
-### enmap.remove(key, val, path) ⇒ [Enmap
](#enmap-map)
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| 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. |
-
-**Example**
-```js
-// Assuming
-enmap.set('array', [1, 2, 3])
-enmap.set('objectarray', [{ a: 1, b: 2, c: 3 }, { d: 4, e: 5, f: 6 }])
-
-enmap.remove('array', 1); // value is now [2, 3]
-enmap.remove('objectarray', (value) => value.e === 5); // value is now [{ a: 1, b: 2, c: 3 }]
-```
-
-
-### enmap.export() ⇒ string
-Exports the enmap data to stringified JSON format.
-**__WARNING__**: Does not work on memory enmaps containing complex data!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: string
- The enmap data in a stringified JSON format.
-
-
-### enmap.import(data, overwrite, clear) ⇒ [Enmap
](#enmap-map)
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap with the new data.
-
-| 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 existing data will be lost! This cannot be undone.) |
-
-
-
-### enmap.array() ⇒ 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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-
-### enmap.keyArray() ⇒ 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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-
-### enmap.random([count]) ⇒ \*
\| Array.<\*>
-Obtains random value(s) from this Enmap. This relies on [array](#Enmap+array).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
\| Array.<\*>
- The single value if `count` is undefined,
-or an array of values of `count` length
-
-| Param | Type | Description |
-| --- | --- | --- |
-| [count] | number
| Number of values to obtain randomly |
-
-
-
-### enmap.randomKey([count]) ⇒ \*
\| Array.<\*>
-Obtains random key(s) from this Enmap. This relies on [keyArray](#Enmap+keyArray)
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
\| Array.<\*>
- The single key if `count` is undefined,
-or an array of keys of `count` length
-
-| Param | Type | Description |
-| --- | --- | --- |
-| [count] | number
| Number of keys to obtain randomly |
-
-
-
-### enmap.findAll(prop, value) ⇒ Array
-Searches for all items where their specified property's value is identical to the given value
-(`item[prop] === value`).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| prop | string
| The property to test against |
-| value | \*
| The expected value |
-
-**Example**
-```js
-enmap.findAll('username', 'Bob');
-```
-
-
-### enmap.find(propOrFn, [value]) ⇒ \*
-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
-[Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
-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.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| 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 |
-
-**Example**
-```js
-enmap.find('username', 'Bob');
-```
-**Example**
-```js
-enmap.find(val => val.username === 'Bob');
-```
-
-
-### enmap.findKey(propOrFn, [value]) ⇒ string
\| number
-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
-[Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| 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 |
-
-**Example**
-```js
-enmap.findKey('username', 'Bob');
-```
-**Example**
-```js
-enmap.findKey(val => val.username === 'Bob');
-```
-
-
-### enmap.sweep(fn, [thisArg]) ⇒ number
-Removes entries that satisfy the provided filter function.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: number
- The number of removed entries
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | Object
| Value to use as `this` when executing function |
-
-
-
-### enmap.filter(fn, [thisArg]) ⇒ [Enmap
](#enmap-map)
-Identical to
-[Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),
-but returns a Enmap instead of an Array.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | Object
| Value to use as `this` when executing function |
-
-
-
-### enmap.filterArray(fn, [thisArg]) ⇒ Array
-Identical to
-[Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | Object
| Value to use as `this` when executing function |
-
-
-
-### enmap.map(fn, [thisArg]) ⇒ Array
-Identical to
-[Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| 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 |
-
-
-
-### enmap.some(fn, [thisArg]) ⇒ boolean
-Identical to
-[Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | Object
| Value to use as `this` when executing function |
-
-
-
-### enmap.every(fn, [thisArg]) ⇒ boolean
-Identical to
-[Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | Object
| Value to use as `this` when executing function |
-
-
-
-### enmap.reduce(fn, [initialValue]) ⇒ \*
-Identical to
-[Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`, and `enmap` |
-| [initialValue] | \*
| Starting value for the accumulator |
-
-
-
-### enmap.clone() ⇒ [Enmap
](#enmap-map)
-Creates an identical shallow copy of this Enmap.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Example**
-```js
-const newColl = someColl.clone();
-```
-
-
-### enmap.concat(...enmaps) ⇒ [Enmap
](#enmap-map)
-Combines this Enmap with others into a new Enmap. None of the source Enmaps are modified.
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| ...enmaps | [Enmap
](#enmap-map) | Enmaps to merge |
-
-**Example**
-```js
-const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl);
-```
-
-
-### ~~enmap.partition(fn, [thisArg]) ⇒ [Array.<Enmap>
](#Enmap)~~
-***Deprecated***
-
-Partitions the enmap into two enmaps where the first enmap
-contains the items that passed and the second contains the items that failed.
-DEPRECATION WILL BE REMOVED IN ENMAP 6!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| fn | function
| Function used to test (should return a boolean) |
-| [thisArg] | \*
| Value to use as `this` when executing function |
-
-**Example**
-```js
-const [big, small] = enmap.partition(guild => guild.memberCount > 250);
-```
-
-
-### ~~enmap.equals(enmap) ⇒ boolean
~~
-***Deprecated***
-
-Checks if this Enmap shares identical key-value pairings with another.
-This is different to checking for equality using equal-signs, because
-the Enmaps may be different objects, but contain the same data.
-DEPRECATION WILL BE REMOVED IN ENMAP 6!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: boolean
- Whether the Enmaps have identical contents
-
-| Param | Type | Description |
-| --- | --- | --- |
-| enmap | [Enmap
](#enmap-map) | Enmap to compare with |
-
-
-
-### ~~enmap.setProp(key, path, val) ⇒ [Enmap
](#enmap-map)~~
-***Deprecated***
-
-Modify the property of a value inside the enmap, if the value is an object or array.
-This is a shortcut to loading the key, changing the value, and setting it back.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use set() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| Param | Type | 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
| Required. The property to modify inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-| val | \*
| Required. The value to apply to the specified property. |
-
-
-
-### ~~enmap.pushIn(key, path, val, allowDupes) ⇒ [Enmap
](#enmap-map)~~
-***Deprecated***
-
-Push to an array element inside an Object or Array element in Enmap.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use push() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| Param | Type | Default | Description |
-| --- | --- | --- | --- |
-| key | string
| | Required. The key of the element. This value MUST be a string or number. |
-| path | string
| | Required. The name of the array property to push to. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-| val | \*
| | Required. The value push to the array property. |
-| allowDupes | boolean
| false
| Allow duplicate values in the array (default: false). |
-
-
-
-### ~~enmap.getProp(key, path) ⇒ \*
~~
-***Deprecated***
-
-Returns the specific property within a stored value. If the key does not exist or the value is not an object, throws an error.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use get() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: \*
- The value of the property obtained.
-
-| Param | Type | Description |
-| --- | --- | --- |
-| key | string
| Required. The key of the element to get from The Enmap. |
-| path | string
| Required. The property to retrieve from the object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-
-
-
-### ~~enmap.deleteProp(key, path)~~
-***Deprecated***
-
-Delete a property from an object or array value in Enmap.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use delete() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| key | string
| Required. The key of the element to delete the property from in Enmap. |
-| path | string
| Required. The name of the property to remove from the object. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-
-
-
-### ~~enmap.removeFrom(key, path, val) ⇒ [Enmap
](#enmap-map)~~
-***Deprecated***
-
-Remove a value from an Array or Object property inside an Array or Object element in Enmap.
-Confusing? Sure is.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use remove() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: [Enmap
](#enmap-map) - The enmap.
-
-| Param | Type | Description |
-| --- | --- | --- |
-| key | string
| Required. The key of the element. This value MUST be a string or number. |
-| path | string
| Required. The name of the array property to remove from. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-| val | \*
| Required. The value to remove from the array property. |
-
-
-
-### ~~enmap.hasProp(key, path) ⇒ boolean
~~
-***Deprecated***
-
-Returns whether or not the property exists within an object or array value in enmap.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use has() instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-**Returns**: boolean
- Whether the property exists.
-
-| Param | Type | Description |
-| --- | --- | --- |
-| key | string
| Required. The key of the element to check in the Enmap or array. |
-| path | \*
| Required. The property to verify inside the value object or array. Can be a path with dot notation, such as "prop1.subprop2.subprop3" |
-
-
-
-### ~~enmap.exists(prop, value) ⇒ boolean
~~
-***Deprecated***
-
-Searches for the existence of a single item where its specified property's value is identical to the given value
-(`item[prop] === value`).
-Do not use this to check for an item by its ID. Instead, use `enmap.has(id)`. See
-[MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) for details.
-DEPRECATION WILL BE REMOVED IN ENMAP 6! Use has("key", "path") instead!
-
-**Kind**: instance method of [Enmap
](#enmap-map)
-
-| Param | Type | Description |
-| --- | --- | --- |
-| prop | string
| The property to test against |
-| value | \*
| The expected value |
-
-**Example**
-```js
-if (enmap.exists('username', 'Bob')) {
- console.log('user here!');
-}
-```
-
-
-### Enmap.multi(names, options) ⇒ Object
-Initialize multiple Enmaps easily.
-
-**Kind**: static method of [Enmap
](#enmap-map)
-**Returns**: Object
- An array of initialized Enmaps.
-
-| 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.. |
-
-**Example**
-```js
-// Using local variables.
-const Enmap = require('enmap');
-const { settings, tags, blacklist } = Enmap.multi(['settings', 'tags', 'blacklist']);
-
-// Attaching to an existing object (for instance some API's client)
-const Enmap = require("enmap");
-Object.assign(client, Enmap.multi(["settings", "tags", "blacklist"]));
-```
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000..b04566c
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,502 @@
+---
+description: >-
+ The complete and unadultered API documentation for every single method and
+ property accessible in Enmap.
+---
+
+# Full Documentation
+
+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.
+
+{% hint style="warning" %}
+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!
+{% endhint %}
+
+
+
+### new Enmap([options])
+Initializes a new Enmap, with options.
+
+
+| Param | Type | Description |
+| --- | --- | --- |
+| [options] | Object
| 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. Unless inMemory is set to true, the enmap will be persisted to disk. |
+| [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! *Note*: Enmap will not automatically create the folder if it is set manually, so make sure it exists before starting your code! |
+| [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.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.inMemory] | boolean
| Optional. If set to true, the enmap will be in-memory only, and will not write to disk. Useful for temporary stores. |
+| [options.sqliteOptions] | Object
| Optional. An object of options to pass to the better-sqlite3 Database constructor. |
+
+**Example**
+```js
+const Enmap = require("enmap");
// Named, Persistent enmap
const myEnmap = new Enmap({ name: "testing" });
// Memory-only enmap
const memoryEnmap = new Enmap({ inMemory: true });
// Enmap that automatically assigns a default object when getting or setting anything.
const autoEnmap = new Enmap({name: "settings", autoEnsure: { setting1: false, message: "default message"}})
+```
+
+
+### enmap.size ⇒ number
+Get the number of key/value pairs saved in the enmap.
+
+**Kind**: instance property of Enmap
+**Returns**: number
- The number of elements in the enmap.
+**Read only**: true
+
+
+### enmap.db ⇒ Database
+Get the better-sqlite3 database object. Useful if you want to directly query or interact with the
underlying SQLite database. Use at your own risk, as errors here might cause loss of data or corruption!
+
+**Kind**: instance property of Enmap
+
+
+### enmap.autonum ⇒ 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.
+
+**Kind**: instance property of Enmap
+**Returns**: number
- The generated key number.
+**Read only**: true
+**Example**
+```js
+enmap.set(enmap.autonum, "This is a new value");
+```
+
+
+### enmap.set(key, value, path)
+Sets a value in Enmap. If the key already has a value, overwrites the data (or the value in a path, if provided).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| Required. The location in which the data should be saved. |
+| value | \*
| Required. The value to write. Values must be serializable, which is done through (better-serialize)[https://github.com/RealShadowNova/better-serialize] If the value is not directly serializable, please use a custom serializer/deserializer. |
+| path | string
| Optional. The path to the property to modify inside the value object or array. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+**Example**
+```js
+// Direct Value Examples
enmap.set('simplevalue', 'this is a string');
enmap.set('isEnmapGreat', true);
enmap.set('TheAnswer', 42);
enmap.set('IhazObjects', { color: 'black', action: 'paint', desire: true });
enmap.set('ArraysToo', [1, "two", "tree", "foor"])
// Settings Properties
enmap.set('IhazObjects', 'blue', 'color'); //modified previous object
enmap.set('ArraysToo', 'three', 2); // changes "tree" to "three" in array.
+```
+
+
+### enmap.update(key, valueOrFunction) ⇒ \*
+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.
+
+**Kind**: instance method of Enmap
+**Returns**: \*
- The modified (merged) value.
+
+| 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. |
+
+**Example**
+```js
+// Define an object we're going to update
enmap.set("obj", { a: 1, b: 2, c: 3 });
// Direct merge
enmap.update("obj", { d: 4, e: 5 });
// obj is now { a: 1, b: 2, c: 3, d: 4, e: 5 }
// Functional update
enmap.update("obj", (previous) => ({
...obj,
f: 6,
g: 7
}));
// this example takes heavy advantage of the spread operators.
// More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
+```
+
+
+### enmap.get(key, path) ⇒ \*
+Retrieves a value from the enmap, using its key.
+
+**Kind**: instance method of Enmap
+**Returns**: \*
- The parsed value for this key.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| The key to retrieve from the enmap. |
+| path | string
| Optional. The property to retrieve from the object or array. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+**Example**
+```js
+const myKeyValue = enmap.get("myKey");
console.log(myKeyValue);
const someSubValue = enmap.get("anObjectKey", "someprop.someOtherSubProp");
+```
+
+
+### enmap.observe(key, path) ⇒ \*
+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.
+
+**Kind**: instance method of Enmap
+**Returns**: \*
- The value for this key.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | \*
| The key to retrieve from the enmap. |
+| path | string
| Optional. The property to retrieve from the object or array. |
+
+
+
+### enmap.keys() ⇒ Array.<string>
+Get all the keys of the enmap as an array.
+
+**Kind**: instance method of Enmap
+**Returns**: Array.<string>
- An array of all the keys in the enmap.
+
+
+### enmap.values() ⇒ Array.<\*>
+Get all the values of the enmap as an array.
+
+**Kind**: instance method of Enmap
+**Returns**: Array.<\*>
- An array of all the values in the enmap.
+
+
+### enmap.entries() ⇒ Array.<Array.<\*, \*>>
+Get all entries of the enmap as an array, with each item containing the key and value.
+
+**Kind**: instance method of Enmap
+**Returns**: Array.<Array.<\*, \*>>
- An array of arrays, with each sub-array containing two items, the key and the value.
+
+
+### enmap.push(key, value, path, allowDupes)
+Push to an array value in Enmap.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| key | string
| | Required. The key of the array element to push to in Enmap. |
+| value | \*
| | Required. The value to push to the array. |
+| path | string
| | Optional. The path to the property to modify inside the value object or array. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+| allowDupes | boolean
| false
| Optional. Allow duplicate values in the array (default: false). |
+
+**Example**
+```js
+// Assuming
enmap.set("simpleArray", [1, 2, 3, 4]);
enmap.set("arrayInObject", {sub: [1, 2, 3, 4]});
enmap.push("simpleArray", 5); // adds 5 at the end of the array
enmap.push("arrayInObject", "five", "sub"); // adds "five" at the end of the sub array
+```
+
+
+### enmap.math(key, operation, operand, path) ⇒ number
+Executes a mathematical operation on a value and saves it in the enmap.
+
+**Kind**: instance method of Enmap
+**Returns**: number
- The updated value after the operation
+
+| Param | Type | 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
| Optional. The property path to execute the operation on, if the value is an object or array. |
+
+**Example**
+```js
+// Assuming
points.set("number", 42);
points.set("numberInObject", {sub: { anInt: 5 }});
points.math("number", "/", 2); // 21
points.math("number", "add", 5); // 26
points.math("number", "modulo", 3); // 2
points.math("numberInObject", "+", 10, "sub.anInt");
+```
+
+
+### enmap.inc(key, path) ⇒ number
+Increments a key's value or property by 1. Value must be a number, or a path to a number.
+
+**Kind**: instance method of Enmap
+**Returns**: number
- The udpated value after incrementing.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| The enmap key where the value to increment is stored. |
+| path | string
| Optional. The property path to increment, if the value is an object or array. |
+
+**Example**
+```js
+// Assuming
points.set("number", 42);
points.set("numberInObject", {sub: { anInt: 5 }});
points.inc("number"); // 43
points.inc("numberInObject", "sub.anInt"); // {sub: { anInt: 6 }}
+```
+
+
+### enmap.dec(key, path) ⇒ Enmap
+Decrements a key's value or property by 1. Value must be a number, or a path to a number.
+
+**Kind**: instance method of Enmap
+**Returns**: Enmap
- The enmap.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| The enmap key where the value to decrement is stored. |
+| path | string
| Optional. The property path to decrement, if the value is an object or array. |
+
+**Example**
+```js
+// Assuming
points.set("number", 42);
points.set("numberInObject", {sub: { anInt: 5 }});
points.dec("number"); // 41
points.dec("numberInObject", "sub.anInt"); // {sub: { anInt: 4 }}
+```
+
+
+### enmap.ensure(key, defaultValue, path) ⇒ \*
+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.
+
+**Kind**: instance method of Enmap
+**Returns**: \*
- The value from the database for the key, or the default value provided for a new key.
+
+| Param | Type | 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
| Optional. If presents, ensures both the key exists as an object, and the full path exists. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+**Example**
+```js
+// Simply ensure the data exists (for using property methods):
enmap.ensure("mykey", {some: "value", here: "as an example"});
enmap.has("mykey"); // always returns true
enmap.get("mykey", "here") // returns "as an example";
// Get the default value back in a variable:
const settings = mySettings.ensure("1234567890", defaultSettings);
console.log(settings) // enmap's value for "1234567890" if it exists, otherwise the defaultSettings value.
+```
+
+
+### enmap.has(key, path) ⇒ boolean
+Returns whether or not the key exists in the Enmap.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| Required. The key of the element to add to The Enmap or array. |
+| path | string
| Optional. The property to verify inside the value object or array. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+**Example**
+```js
+if(enmap.has("myKey")) {
// key is there
}
if(!enmap.has("myOtherKey", "oneProp.otherProp.SubProp")) return false;
+```
+
+
+### enmap.includes(key, value, path) ⇒ boolean
+Performs Array.includes() on a certain enmap value. Works similar to
[Array.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes).
+
+**Kind**: instance method of Enmap
+**Returns**: boolean
- Whether the array contains the value.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| Required. The key of the array to check the value of. |
+| value | string
\| number
| Required. The value to check whether it's in the array. |
+| path | string
| Optional. The property to access the array inside the value object or array. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+
+
+### enmap.delete(key, path)
+Deletes a key in the Enmap.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| Required. The key of the element to delete from The Enmap. |
+| path | string
| Optional. The name of the property to remove from the object. Should be a path with dot notation, such as "prop1.subprop2.subprop3" |
+
+
+
+### enmap.clear() ⇒ void
+Deletes everything from the enmap.
+
+**Kind**: instance method of Enmap
+
+
+### enmap.remove(key, val, path)
+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.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string
| Required. The key of the element to remove from in Enmap. |
+| 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
| Optional. The name of the array property to remove from. Should be a path with dot notation, such as "prop1.subprop2.subprop3". If not presents, removes directly from the value. |
+
+**Example**
+```js
+// Assuming
enmap.set('array', [1, 2, 3])
enmap.set('objectarray', [{ a: 1, b: 2, c: 3 }, { d: 4, e: 5, f: 6 }])
enmap.remove('array', 1); // value is now [2, 3]
enmap.remove('objectarray', (value) => value.e === 5); // value is now [{ a: 1, b: 2, c: 3 }]
+```
+
+
+### enmap.export() ⇒ string
+Exports the enmap data to stringified JSON format.
**__WARNING__**: Does not work on memory enmaps containing complex data!
+
+**Kind**: instance method of Enmap
+**Returns**: string
- The enmap data in a stringified JSON format.
+
+
+### enmap.import(data, overwrite, clear)
+Import an existing json export from enmap. 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.
(This means Enmap 5 data is compatible in Enmap 6).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| data | string
| | The data to import to Enmap. Must contain all the required fields provided by an enmap 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 existing data will be lost! This cannot be undone.) |
+
+
+
+### enmap.random([count]) ⇒ \*
\| Array.<\*>
+Obtains random value(s) from this Enmap. This relies on [Enmap#array](Enmap#array).
+
+**Kind**: instance method of Enmap
+**Returns**: \*
\| Array.<\*>
- The single value if `count` is undefined,
or an array of values of `count` length
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [count] | number
| 1
| Number of values to obtain randomly |
+
+
+
+### enmap.randomKey([count]) ⇒ \*
\| Array.<\*>
+Obtains random key(s) from this Enmap. This relies on [Enmap#keyArray](Enmap#keyArray)
+
+**Kind**: instance method of Enmap
+**Returns**: \*
\| Array.<\*>
- The single key if `count` is undefined,
or an array of keys of `count` length
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [count] | number
| 1
| Number of keys to obtain randomly |
+
+
+
+### enmap.every(valueOrFunction, [path]) ⇒ boolean
+Similar to
[Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).
Supports either a predicate function or a value to compare.
Returns true only if the predicate function returns true for all elements in the array (or the value is strictly equal in all elements).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| valueOrFunction | function
\| string
| Function used to test (should return a boolean), or a value to compare. |
+| [path] | string
| Required if the value is an object. The path to the property to compare with. |
+
+
+
+### enmap.some(valueOrFunction, [path]) ⇒ Array
+Similar to
[Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
Supports either a predicate function or a value to compare.
Returns true if the predicate function returns true for at least one element in the array (or the value is equal in at least one element).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| valueOrFunction | function
\| string
| Function used to test (should return a boolean), or a value to compare. |
+| [path] | string
| Required if the value is an object. The path to the property to compare with. |
+
+
+
+### enmap.map(pathOrFn) ⇒ Array
+Similar to
[Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
Returns an array of the results of applying the callback to all elements.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | function
\| string
| A function that produces an element of the new Array, or a path to the property to map. |
+
+
+
+### enmap.find(pathOrFn, [value]) ⇒ \*
+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 similar to
[Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | string
\| function
| The path to the value 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 |
+
+**Example**
+```js
+enmap.find('username', 'Bob');
+```
+**Example**
+```js
+enmap.find(val => val.username === 'Bob');
+```
+
+
+### enmap.findIndex(pathOrFn, [value]) ⇒ string
\| number
+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 similar to
[Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | string
\| function
| The path to the value 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 |
+
+**Example**
+```js
+enmap.findIndex('username', 'Bob');
+```
+**Example**
+```js
+enmap.findIndex(val => val.username === 'Bob');
+```
+
+
+### enmap.reduce(predicate, [initialValue]) ⇒ \*
+Similar to
[Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| predicate | function
| Function used to reduce, taking three arguments; `accumulator`, `currentValue`, `currentKey`. |
+| [initialValue] | \*
| Starting value for the accumulator |
+
+
+
+### enmap.filter(pathOrFn, [value]) ⇒ Enmap
+Similar to
[Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
Returns an array of values where the given function returns true for that value.
Alternatively you can provide a value and path to filter by using exact value matching.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | function
| The path to the value to test against, or the function to test with. If using a function, this function should return a boolean. |
+| [value] | string
| Value to use as `this` when executing function |
+
+
+
+### enmap.sweep(pathOrFn, [value]) ⇒ number
+Deletes entries that satisfy the provided filter function or value matching.
+
+**Kind**: instance method of Enmap
+**Returns**: number
- The number of removed entries.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | function
\| string
| The path to the value 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. |
+
+
+
+### enmap.changed(cb)
+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.
+
+**Kind**: instance method of Enmap
+
+| Param | Type | Description |
+| --- | --- | --- |
+| cb | function
| A callback function that will be called whenever data changes in the enmap. |
+
+**Example**
+```js
+enmap.changed((keyName, oldValue, newValue) => {
console.log(`Value of ${keyName} has changed from: \n${oldValue}\nto\n${newValue}`);
});
+```
+
+
+### enmap.partition(pathOrFn, value) ⇒ Array.<Array.<\*>>
+Separates the Enmap into multiple arrays given a function that separates them.
+
+**Kind**: instance method of Enmap
+**Returns**: Array.<Array.<\*>>
- An array of arrays with the partitioned data.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| pathOrFn | \*
| the path to the value to test against, or the function to test with. |
+| value | \*
| the value to use as a condition for partitioning. |
+
+
+
+### Enmap.multi(names, options) ⇒ Object
+Initialize multiple Enmaps easily.
+
+**Kind**: static method of Enmap
+**Returns**: Object
- An array of initialized Enmaps.
+
+| 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.. |
+
+**Example**
+```js
+// Using local variables.
const Enmap = require('enmap');
const { settings, tags, blacklist } = Enmap.multi(['settings', 'tags', 'blacklist']);
// Attaching to an existing object (for instance some API's client)
const Enmap = require("enmap");
Object.assign(client, Enmap.multi(["settings", "tags", "blacklist"]));
+```
diff --git a/docs/assets/enmap-logo.svg b/docs/assets/enmap-logo.svg
new file mode 100644
index 0000000..c4f6832
--- /dev/null
+++ b/docs/assets/enmap-logo.svg
@@ -0,0 +1,168 @@
+
+
diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico
new file mode 100644
index 0000000..18f811c
Binary files /dev/null and b/docs/assets/favicon.ico differ
diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png
new file mode 100644
index 0000000..07520bb
Binary files /dev/null and b/docs/assets/favicon.png differ
diff --git a/docs/blog-posts/README.md b/docs/blog-posts/README.md
new file mode 100644
index 0000000..7933c02
--- /dev/null
+++ b/docs/blog-posts/README.md
@@ -0,0 +1,2 @@
+# Blog Posts
+
diff --git a/docs/blog-posts/enmaps-history.md b/docs/blog-posts/enmaps-history.md
new file mode 100644
index 0000000..807569f
--- /dev/null
+++ b/docs/blog-posts/enmaps-history.md
@@ -0,0 +1,26 @@
+# Enmap's History
+
+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!
+
+## Update, August 2019
+
+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.
+
diff --git a/docs/blog-posts/josh.md b/docs/blog-posts/josh.md
new file mode 100644
index 0000000..a1b942a
--- /dev/null
+++ b/docs/blog-posts/josh.md
@@ -0,0 +1,32 @@
+---
+description: >-
+ Let's take a quick peek at what Josh is, and what it means for the future of
+ Enmap
+---
+
+# Enmap and Josh
+
+As I've noted in my[ previous blog post](why-sqlite-only.md), 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.
+
+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.
+
+### Introducing Josh
+
+Then plan was [Josh](https://josh.evie.dev/), 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, [Josh ](https://josh.evie.dev/)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".
+
+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.
+
+### So what does that mean for Enmap?
+
+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.
+
+Josh and Enmap should, and will, be fully compatible with one another, in that you will be able to easily migrate between them \(with [export](../api.md#enmap-export-string)\(\) and [import](../api.md#enmap-import-data-overwrite-clear-enmap)\(\) \), 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.
+
+### What does the future hold?
+
+I've already back-ported a few things that I originally intended for Josh as part of Enmap's new updates. The [observe](../api.md#enmap-observe-key-path)\(\) method, as well as the[ serializer/deserializer](../usage/serialize.md) 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!
+
+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!
+
diff --git a/docs/blog-posts/why-remove-cache.md b/docs/blog-posts/why-remove-cache.md
new file mode 100644
index 0000000..8232b5d
--- /dev/null
+++ b/docs/blog-posts/why-remove-cache.md
@@ -0,0 +1,150 @@
+---
+description: >-
+ Why would Enmap V6 remove caching? Doesn't that make it slower? What gives?
+---
+
+# Why update to v6???
+
+So, you might be wondering what the main driver of Enmap Version 6 is. Let me give you a little bit of historical context here.
+
+Before Enmap, there was [djs-collection-persistent](https://www.npmjs.com/package/djs-collection-persistent). This module was born
+from using discord.js' Collection, and the idea was, "What if I could use that, but save it in a database?" and thus, this whole
+adventure started. It saved data on `leveldb`, and it was alright. But who the hell wants to remember that name?
+
+And thus, Enmap 1.0 was born. The *Enhanced* Map, which detached the name from djs itself. Enmap 1.0 already established Providers,
+including leveldb, postgresql, sqlite, rethinkdb, and mongo.
+
+Enmap 2 and 3 added more features, moved things around, but generally stayed the same. Lot of bug fixes, added math, that sort of thing.
+
+In Enmap 4, the main major change was that I removed all providers. I discovered (better-sqlite3)[https://www.npmjs.com/package/better-sqlite3],
+the first (and only?) synchronous database module. This started changing everything, but I didn't fully trust its performance yet. So Enmap 4
+is sqlite only, persistent, but it still has its cache... that is to say, it's still an enhanced Map structure with persistence. Enmap 5 is
+more of the same, updates, small breaking changes, new features, etc.
+
+But there's a problem : Enmap... is stil cached. It still uses a lot of memory, and that makes it slower than it should be. better-sqlite3 is *fast*
+and now I'm updating both the cache (Map) and the database! But I left this sleeping for the last few years as I was doing other things with life.
+
+And here we are. Enmap 6.0 just removes caching, and updates all the map/array methods to directly interact with the database, no cache needed. This
+not only simplifies the code, and reduces RAM usage by a *wide* margin, it also makes Enmap much faster in a number of situations.
+
+## The SPEED
+
+### Loading
+
+Loading of data remains approximately the same when empty, but can be much faster in Enmap 6 the larger your database is, if `autoFetch` is `true`.
+With the 1 million rows, Enmap 6 loads in 6ms (milliseconds) but Enmap 5 loads in 20s (seconds). That's a massive difference, because of caching.
+
+### Adding Data
+
+This test inserts 1 million rows in a simple for loop. Each entry is an object with multiple randomly generated numbers.
+
+Here's the actual test!
+```js
+const rnum = size => Math.floor(Math.random() * (size + 1));
+
+for (let i = 1; i <= 1_000_000; i++) {
+ enmap.set(`obj${i}`, {
+ key: `obj${i}`,
+ a: rnum(100),
+ b: rnum(100),
+ c: rnum(100),
+ d: rnum(100),
+ e: rnum(100),
+ });
+}
+```
+
+```diff
+-1 million enmap5: 2:57.741 (m:ss.mmm)
++1 million enmap6: 2:44.252 (m:ss.mmm)
+```
+
+As you can see, the insert time is almost the same. I tried a few times, the time are around 2 minutes 50 seconds, +- 10 seconds.
+The speed does not change if the data already exists since it's all new data anyway (this means "key creation" doesn't cost anything).
+
+### Looping over data
+
+Enmap, when looping over data, is generally faster.
+
+Here's the tests and results. I tried more than once, and it's all the same ballpark.
+
+#### Partition: Faster
+
+```js
+console.time('partition enmap');
+const [one, two] = enmap.partition((value) => value.a % 2 === 0);
+console.timeEnd('partition enmap');
+
+console.time('partition enmap6');
+const [one6, two6] = enmap6.partition((value) => value.a % 2 === 0);
+console.timeEnd('partition enmap6');
+```
+
+```diff
+-partition enmap5: 51.221s
++partition enmap6: 6.048s
+```
+
+As you can see Enmap 6 is 8.5x faster with partitioning (again, this is on 1 million rows).
+
+This is partially due to partition() returning an array of 2 Enmap structure. It would potentially
+be faster if Enmap 5's partition() returned arrays.
+
+#### Filtering: Faster, sort of
+
+```js
+console.time('filter enmap');
+const filtered = enmap.filter((value) => value.a % 2 === 0);
+console.timeEnd('filter enmap');
+
+console.time('filter enmap6');
+const filtered6 = enmap6.filter((value) => value.a % 2 === 0);
+console.timeEnd('filter enmap6');
+```
+
+```diff
+- filter enmap5: 28.315s
++ filter enmap6: 5.560s
+```
+
+Filtering is also faster with Enmap 6 partially because Enmap 5 uses an Enmap as a return value,
+rather than an array. filterArray is definitely faster if the data is cached:
+```
+filterArray enmap: 56.564ms
+```
+
+#### Mapping: Slower
+
+```js
+console.time('map enmap');
+const mapped = enmap.map((value) => value.a * 2);
+console.timeEnd('map enmap');
+
+console.time('map enmap6');
+const mapped6 = enmap6.map((value) => value.a * 2);
+console.timeEnd('map enmap6');
+```
+
+```diff
+-map enmap5: 47.295ms
++map enmap6: 6.271s
+```
+
+I **almost** missed the difference in magnitude here: enmap.map() is slower by a lot.
+I'm not sure why and won't expend more time on this, and I don't feel guilty, because
+loading the 1M values took 17s for enmap5 versus the 6ms uncached enmap6. Still a clear
+value winner either way.
+
+## Conclusion
+
+I initially was very excited by the better Enmap 6 performance, but that was before I realized that
+some of this better performance is due to using memory-only Enmaps as return values. This means
+that some Enmap 5 methods are faster, such as filterArray and map.
+
+As I really do want Enmap 6 to come out, however, I'm satisfied with the current removal of the cache.
+it still gives the advantage of having a lot less RAM usage since a cache isn't filled. It also means
+more consistency in query times, in memory usage, and availability - some cached methods like partition
+*only* worked with cached values and did not fetch keys before running.
+
+I will, however, re-add caching to Enmap 6.1, as an optional addition and potentially more control over
+the caching, time-to-live, etc.
diff --git a/docs/blog-posts/why-sqlite-only.md b/docs/blog-posts/why-sqlite-only.md
new file mode 100644
index 0000000..3a05988
--- /dev/null
+++ b/docs/blog-posts/why-sqlite-only.md
@@ -0,0 +1,47 @@
+---
+description: >-
+ 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
+---
+
+# Why SQLITE only?
+
+## Why providers in the first place?
+
+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\).
+
+1. It enabled supporting more databases, not only one. This gave more power to users, and, I thought, more capabilities.
+2. It separated the memory enmap \(non-persistent\) from the database layer, so installing enmap didn't require installing sqlite.
+
+## And why remove them?
+
+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:
+
+1. 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.
+2. 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.
+3. There were features I wanted that simply weren't possible, physically, with the providers \([like the fetchAll/autoFetch options](../usage/fetchall.md)\).
+
+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.
+
+## What are the advantages of sqlite?
+
+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.
+
+## But what about people that NEED a provider?
+
+~~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.~~
+
+Update: Enmap's no longer hosted on gitbook, and Enmap V3 is old enough to be dangerous to use due to potential security vulnerabilities, and providers most likely don't work on recent node versions anyways. All Enmap 3 providers are deprecated and archived.
diff --git a/docs/build.js b/docs/build.js
deleted file mode 100644
index 71e55a7..0000000
--- a/docs/build.js
+++ /dev/null
@@ -1,49 +0,0 @@
-const jsdoc2md = require('jsdoc-to-markdown');
-const fs = require('fs');
-const slug = require('limax');
-
-var htmlEntities = {
- nbsp: ' ',
- cent: '¢',
- pound: '£',
- yen: '¥',
- euro: '€',
- copy: '©',
- reg: '®',
- lt: '<',
- gt: '>',
- quot: '"',
- amp: '&',
- apos: '\''
-};
-
-const unescapeHTML = str => str.replace(/\&([^;]+);/g, (entity, entityCode) => {
- let match;
-
- if (entityCode in htmlEntities) {
- return htmlEntities[entityCode];
- /* eslint no-cond-assign: 0 */
- } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
- return String.fromCharCode(parseInt(match[1], 16));
- /* eslint no-cond-assign: 0 */
- } else if (match = entityCode.match(/^#(\d+)$/)) {
- return String.fromCharCode(~~match[1]);
- } else {
- return entity;
- }
-});
-
-const finalize = str => str
- .replace(/\[Enmap\]\(#Enmap\)/gi, '[Enmap](#enmap-map)')
- .replace(/\[Enmap<\/code>\]\(#Enmap\)/gi, '[Enmap
](#enmap-map)')
- .replace('* [new Enmap(iterable, [options])](#new_Enmap_new)', '* [new Enmap(iterable, [options])](#new-enmap-iterable-options)');
-
-const regexread = /^ {8}\* \[\.(.*?)\]\((.*?)\)(.*?)(\(#.*?\)|)$/gm;
-
-const parseData = data => finalize(data.replace(regexread, (_, b, __, d) =>
- ` * [.${b}](#${slug(`enmap.${b} ${unescapeHTML(d.replace(/<\/?code>/g, ''))}`)})${d}`));
-
-jsdoc2md.render({ files: './src/index.js' }).then(data =>
- fs.writeFile('./docs/api-docs.md',
- parseData(data),
- () => false));
diff --git a/docs/help/CJStoESM.md b/docs/help/CJStoESM.md
new file mode 100644
index 0000000..f47c52d
--- /dev/null
+++ b/docs/help/CJStoESM.md
@@ -0,0 +1,57 @@
+I'm not going to describe all of the process here, but the gist of it goes something like this
+(credit to `qrpx` on The Programmer's Hangout for the reference):
+
+```diff
+- const AModule = require("a-module")
++ import AModule from "a-module";
+
+- const { AModule } = require("a-module");
++ import { AModule } from "a-module";
+
+- module.exports = AnExport;
++ export default AnExport;
+
+- module.exports = { Export1, Export2 };
++ export default { Export1, Export2 };
+
+- module.exports.Export1 = Export1;
++ export { Export1 };
+
+- module.exports = require("a-module");
++ export Export from "a-module";
++ export * from "a-module";
+
+- module.exports = require("a-module").Export;
++ export { Export } from "a-module";
+
+- module.exports = {
+- myvar1: "String1",
+- mayVar2: "String2"
+- }
++ export const myVar1 = "String1";
++ export const myVar2 = "String2";
+
+- require("my-file")(myvar);
++ (await import(`my-file`)).default(myVar);
+```
+
+Renaming:
+```diff
+- const renamed = require("name");
++ import name as renamed from "name";
+
+- const { name: renamed } = require("name");
++ import { name as renamed } from "name";
+
+- module.exports.Export1 = Export2;
++ export { Export2 as Export1 };
+```
+
+
+Advantages over CommonJS
+```diff
+- const EntireModule = require("a-module");
+- const APartOfIt = require("a-module").part;
+
++ import EntireModule, { part as APartOfIt } from "a-module";
+```
diff --git a/docs/install/README.md b/docs/install/README.md
new file mode 100644
index 0000000..7ee444c
--- /dev/null
+++ b/docs/install/README.md
@@ -0,0 +1,65 @@
+---
+description: >-
+ To install Enmap, please read these instructions very carefully, every word is important!
+---
+
+# Enmap Installation
+
+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.
+
+## Pre-Requisites
+
+{% hint style="warning" %}
+SQLite modules usually only successfully work on LTS versions of node which are even-numbered. This means
+it will work on node 12, 14, 16 but will most likely _not_ work on 13, 15, 17. Make sure you have the right version, check this with `node -v`.
+{% endhint %}
+
+How to install the pre-requisites depends on your operating system, so see below for instructions:
+
+{% tabs %}
+{% tab title="Windows" %}
+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:_
+
+```javascript
+// First run:
+npm i -g --add-python-to-path --vs2015 --production windows-build-tools
+// If you get an error here READ THE TEXT ABOVE AND BELOW THIS CODE BLOCK, IT IS IMPORTANT.
+
+// Then run:
+npm i -g node-gyp@latest
+```
+
+> 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, VSCode, and editors with a built-in console/prompt**. Otherwise, the next command will not work.
+{% endtab %}
+
+{% tab title="Linux" %}
+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 pre-installed. For some other systems, you might have to fiddle with it to install python (2 or 3, whichever is easiest). Google will be your friend as is customary with Linux.
+
+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".
+{% endtab %}
+
+{% tab title="Mac OS" %}
+As of writing this page, MacOS versions seem to all come pre-built with Python on the system. You will, however, need the C++ build tools.
+
+* Install [XCode](https://developer.apple.com/xcode/download/)
+* Once XCode is installed, go to **Preferences**, **Downloads**, and install the **Command Line Tools**.
+
+Once installed, you're ready to continue.
+{% endtab %}
+{% endtabs %}
+
+## Installing Enmap
+
+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:
+
+```text
+npm i enmap
+```
+
+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](troubleshooting-guide.md). If the guide doesn't help, join the Discord \(link at the top of this page\).
diff --git a/docs/install/troubleshooting-guide.md b/docs/install/troubleshooting-guide.md
new file mode 100644
index 0000000..87ba250
--- /dev/null
+++ b/docs/install/troubleshooting-guide.md
@@ -0,0 +1,28 @@
+---
+description: '"Anything that can go wrong will go wrong" - Murphy''s Law'
+---
+
+# Troubleshooting Guide
+
+{% hint style="warning" %}
+Please make sure to read the [install page](/install) **VERY CAREFULLY**! This should solve all the issues you face. If not carry on, but don't say we didn't tell you.
+{% endhint %}
+
+## It's Taking Suuuuuuuuuuuuuuuuuper Long to Install! Whats up with that?
+
+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.
+
+Some tips: Make sure you are doing this install on a fast hard drive (ideally SSD, but HDD will work fine) and **not on a USB Key** or removable media.
+
+## command failed downloading python or build tools
+
+`Please restart this script in an administrative Powershel!` means you did not read the [install page](/install) correctly.
+
+## MSB3428: Could not load the Visual C++ component "VCBuild.exe"
+
+Looks like someone hasn't follows the [installation instructions](/install) correctly...
+
+(are you sensing there's a theme yet in this troubleshooting guide?)
+## My Question Isn't Answered Here!
+
+Please make sure you read the [install page](/install) first. If you can't find what your looking for you can join the discord [here](https://discord.gg/N7ZKH3P).
diff --git a/docs/install/upgrade.md b/docs/install/upgrade.md
new file mode 100644
index 0000000..763a187
--- /dev/null
+++ b/docs/install/upgrade.md
@@ -0,0 +1,97 @@
+---
+description: >-
+ This guide assists in migrating your data from Enmap 3 using Providers, to the
+ latest version of enmap.
+---
+
+# Migrating data from Enmap 3
+
+{% hint style="warning" %}
+You do not need this page if you're new to Enmap or if you're starting a new project!
+{% endhint %}
+
+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.
+
+```javascript
+const Enmap = require("enmap");
+const Provider = require("enmap-mongo");
+const SQLite = require("enmap-sqlite");
+
+let options = {
+ name: "test",
+ user: "username",
+ host: "yourhost",
+ collection: "enmap",
+ password: "password",
+ port: 55258
+};
+
+const source = new Provider(options);
+const target = new SQLite({"name": "test", dataDir: '../coolbot-copy/data'});
+Enmap.migrate(source, target).then( () => process.exit(0) );
+```
+
+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.
+
+### Simpler migration from enmap-sqlite
+
+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.
+
+```javascript
+const Enmap = require("enmap");
+const SQLite = require("enmap-sqlite");
+
+const source = new SQLite({"name": "test"});
+const target = new SQLite({"name": "test"});
+Enmap.migrate(source, target).then( () => process.exit(0) );
+```
+
+## Code Changes
+
+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.
+
+```javascript
+// Change From:
+const Enmap = require("enmap");
+const Provider = require("enmap-mongo");
+
+client.points = new Enmap({provider: new Provider({name: "points", url: "blah"});
+
+// Change To:
+const Enmap = require("enmap");
+client.points = new Enmap({name: "points"});
+```
+
+If using Enmap.multi\(\), the change is just as simple:
+
+```javascript
+// Change from V3:
+const Enmap = require("enmap");
+const Provider = require("enmap-mongo");
+
+Object.assign(client, Enmap.multi(["settings", "tags"], Provider, { url: "blah" }));
+
+// Change to V4:
+const Enmap = require("enmap");
+Object.assign(client, Enmap.multi(["settings", "tags"]));
+```
+
+The rest of your code \(all interactions with Enmap\) can remain the same - there should be no need to edit any of it.
+
+## Installing V4
+
+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 ^\_^.
+
diff --git a/docs/install/upgradev6.md b/docs/install/upgradev6.md
new file mode 100644
index 0000000..2c60c92
--- /dev/null
+++ b/docs/install/upgradev6.md
@@ -0,0 +1,129 @@
+# Upgrading Enmap to Version 6
+
+Version 6 of Enmap is a complete re-write, even though most of the API remains identical, and the data can easily be transfered.
+
+Please pay attention to all the changes on this page :)
+
+## Migration Method
+
+**BEFORE YOU UPGRADE VERSION**, you will need to use `enmap.export()` on Enmap 5, to have a functional backup.
+
+I *strongly* advise copying your `./data` directory... just in case this breaks ;)
+
+Here's a quick and dirty script:
+
+```js
+const fs = require("fs");
+const Enmap = require("enmap");
+
+const enmap = new Enmap({ name: 'nameofenmap' });
+
+fs.writeFile('./export.json', enmap.export(), () => {
+ // I hope the data was in fact saved, because we're deleting it! Double-check your backup file size.
+ enmap.clear();
+});
+```
+
+{% hint style="warning" %}
+You *will* need to do this for every separate enmap (every "name") you have, individually, with separate export files!
+{% endhint %}
+
+Once exporting is done, you can `npm i enmap@latest` to get version 6.X. After this, the *import* needs to be done, as such:
+
+```js
+const fs = require("fs");
+const Enmap = require("enmap");
+
+const enmap = new Enmap({ name: 'nameofenmap' });
+
+fs.readFile('./export.json', (err, data) => {
+ enmap.import(data);
+});
+```
+
+Marginally tested, but should work fine for any and all data.
+
+## Move to ESM
+
+The first major change is the move to ECMAScript Modules, or *ESM*, which some erroneously call "ES6 modules". This change unfortunately not only
+affects the code related to Enmap but also means if you want to keep using it, you'll have to move to ESM too along with the rest of us. Gone is
+CJS, here comes ESM!
+
+ESM has been around for a long time, it matches the module format used in other javascript engines (such as browsers) and it used by the majority
+of builder tools. If you're using Typescript or doing web frameworks, chances are you're using ESM already. And if you're still on CJS, well,
+going to ESM is important in your JavaScript developer journey anyways.
+
+So what does this mean? It means modifying all your imports and exports, starting with Enmap:
+```diff
+- const Enmap = require("enmap");
++ import Enmap from 'enmap';
+```
+
+Is that it? Yes, that's it... for my module. Now you have to do this for the rest of your code. [Here's a bit of a guide for you](../help/CJStoESM.md).
+
+## Removal of the Caching
+
+Caching has been around since Enmap v1, for the simple reason that enmap used wrap around callback-based and promise-based database modules.
+In order to provide a synchronous interface to you, the user, the use of caching to update and read the database in the background. That
+hasn't been the case since Enmap 4 where I stripped out the providers and locked it to `better-sqlite3`, the only synchronous database module
+that exists for an actual database connection. That means I didn't need the cache anymore, but I kept it for performance reasons.
+
+For more details and justifications, see ["Why Remove Cache?"](../blog-posts/why-remove-cache.md).
+
+This means the following Enmap features are obsolete and have been stripped out from V6, hopefully never to be added again.
+
+- `enmap.fetch` and `enmap.fetchEverything` : were used to query the database and put the data in cache.
+- `enmap.evict` : used to remove from cache.
+- `enmap.array` and `enmap.keyArray` : used to list values and keys from cache only.
+- `enmap.indexes` : used to get a list of keys in the database (not the cache).
+- `options.fetchAll` : determines whether to cache the entire database on load.
+- `options.cloneLevel`: was a workaround to objects provided by the user affecting objects in the cache.
+- `options.polling` and `options.pollingInterval`: used to update the cache on an interval, useful to sync many processes.
+- `options.autoFetch` : used to fetch values automatically when using enmap.get()
+
+So all the above just don't exist anymore. However, they will return in Enmap 6.1 with *optional* controllable caching.
+
+## Removal of duplicate concerns
+
+Enmap used to essentially extend two different structures: the Map(), and the Array(), javascript structures. With the removal of the cache,
+the Map()... well... I guess at this point Enmap's name is historical because I'm not extending a Map anymore! However, the extension of Map()
+for cache and Array for feature meant there was a lot of duplication in the methods. Enmap V6 clears out any method that could be achieved with
+another method. I have made every possible effort not to lose power in Enmap, so if you find that something I removed was stupid, please feel
+free to make the case for it on our Discord server.
+
+`enmap.keys()`, `enmap.values()` and `enmap.entries()` can be used to get only keys, only values, or *both*, in arrays. This will pull the *entire*
+database's worth of data, but that's what you were expecting, so it's fine, isn't it? As such, `enmap.array()` and `enmap.keyArray()` become
+obsolete and have been removed.
+
+`enmap.indexes` also isn't useful anymore and was the "uncached" version of `enmap.keys`, so it's removed.
+
+`enmap.count` and `enmap.size` have always been a bit confusing, especially since arrays have a `length`... so I've decided to just call it `enmap.length`.
+To maintain backwards compatibility, though, `enmap.size` will remain as an alias.
+
+`enmap.filter()` and `enmap.filterArray()` were always a bit confusing to me. The idea of "returning an Enmap" from Enmap itself was always weird and I
+will no longer be doing that - that means that `enmap.filter()` will not simply return an array, and that's it. Also, it returns an array of *values*,
+and does not concern itself with returning keys (same for any other method that only returns values in arrays).
+
+## Obsolete things I've deleted
+
+These were already planned, and indicated as deprecated for a while now, but they've now been removed:
+
+- `enmap.equals()` (extremely expensive, not useful)
+- `enmap.exists()` (use `has(key, prop)`)
+- `enmap.setProp()` (use `set(key, prop)`)
+- `enmap.pushIn()` (use `push(key, prop)`)
+- `enmap.getProp()` (use `get(key, prop)`)
+- `enmap.deleteProp()` (use `delete(key, prop)`)
+- `enmap.removeProp()` (use `remove(key, prop)`)
+- `enmap.hasProp()` (use `has(key, prop)`)
+- `enmap.ready` or `enmap.defer` and all that jazz - completely useless in this sync world.
+
+## Misc changes
+
+- The use of `'::memory::` as a name is removed, you can use `inMemory: true` instead. That means `new Enmap('::memory::')` is now `new Enmap({ inMemory: true })`.
+- In all loop methods like `every`, `some`, `map` and `filter`, the **value** now comes first, and **key** second. This matches array methods closer.
+- Methods will no longer return the enmap upon executing an action. It's always felt weird to me that some methods returned the enmap and others returned data.
+- The `destroy` method is removed, since it doesn't make much sense to delete all the db tables. You can still delete all your stuff with `clear()` though.
+- `wal` and `verbose` options have been removed from options, I honestly prefer the default to `journal_mode=WAL`. If you don't like it, run
+ `enmap.db.pragma('journal_mode=DELETE')` (you can run direct DB calls to sqlite3 this way). For verbose, pass it as `options.sqliteOptions`, like,
+ `new Enmap({ name: 'blah', sqliteOptions: { verbose: true }})`.
diff --git a/docs/retype.manifest b/docs/retype.manifest
new file mode 100644
index 0000000..ddae6e2
--- /dev/null
+++ b/docs/retype.manifest
@@ -0,0 +1,135 @@
+{
+ "version": "3.5.0",
+ "created": "2024-04-08T05:27:41Z",
+ "files": [
+ {
+ "path": ".nojekyll"
+ },
+ {
+ "path": "404.html"
+ },
+ {
+ "path": "api\\index.html"
+ },
+ {
+ "path": "assets\\enmap-logo.svg"
+ },
+ {
+ "path": "assets\\favicon.ico"
+ },
+ {
+ "path": "assets\\favicon.png"
+ },
+ {
+ "path": "blog-posts\\enmaps-history\\index.html"
+ },
+ {
+ "path": "blog-posts\\index.html"
+ },
+ {
+ "path": "blog-posts\\josh\\index.html"
+ },
+ {
+ "path": "blog-posts\\why-remove-cache\\index.html"
+ },
+ {
+ "path": "blog-posts\\why-sqlite-only\\index.html"
+ },
+ {
+ "path": "CNAME"
+ },
+ {
+ "path": "help\\cjstoesm\\index.html"
+ },
+ {
+ "path": "index.html"
+ },
+ {
+ "path": "install\\index.html"
+ },
+ {
+ "path": "install\\troubleshooting-guide\\index.html"
+ },
+ {
+ "path": "install\\upgrade\\index.html"
+ },
+ {
+ "path": "install\\upgradev6\\index.html"
+ },
+ {
+ "path": "resources\\css\\retype.css"
+ },
+ {
+ "path": "resources\\fonts\\Inter-italic-latin-var.woff2"
+ },
+ {
+ "path": "resources\\fonts\\Inter-roman-latin-var.woff2"
+ },
+ {
+ "path": "resources\\js\\config.js"
+ },
+ {
+ "path": "resources\\js\\lunr.js"
+ },
+ {
+ "path": "resources\\js\\prism.js"
+ },
+ {
+ "path": "resources\\js\\retype.js"
+ },
+ {
+ "path": "resources\\js\\search.json"
+ },
+ {
+ "path": "resources\\lunr.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\mermaid.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\prism.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\retype.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\retype.LICENSE.txt"
+ },
+ {
+ "path": "robots.txt"
+ },
+ {
+ "path": "sitemap.xml.gz"
+ },
+ {
+ "path": "sponsors\\index.html"
+ },
+ {
+ "path": "usage\\arrays\\index.html"
+ },
+ {
+ "path": "usage\\basic\\index.html"
+ },
+ {
+ "path": "usage\\index.html"
+ },
+ {
+ "path": "usage\\math\\index.html"
+ },
+ {
+ "path": "usage\\objects\\index.html"
+ },
+ {
+ "path": "usage\\paths\\index.html"
+ },
+ {
+ "path": "usage\\serialize\\index.html"
+ },
+ {
+ "path": "usage\\using-enmap.multi\\index.html"
+ },
+ {
+ "path": "usage\\using-from-multiple-files\\index.html"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/retype.yml b/docs/retype.yml
new file mode 100644
index 0000000..40a14e3
--- /dev/null
+++ b/docs/retype.yml
@@ -0,0 +1,17 @@
+input: .
+output: .retype
+url: enmap.evie.dev
+branding:
+ logo: assets/enmap-logo.svg
+favicon: assets/favicon.png
+links:
+- text: Github
+ link: https://github.com/eslachance/enmap
+- text: NPM
+ link: https://npmjs.com/package/enmap
+- text: Discord
+ link: https://discord.gg/N7ZKH3P
+edit:
+ repo: "http://github.com/eslachance/enmap/edit/"
+footer:
+ copyright: "© Copyright {{ year }}. All rights reserved."
diff --git a/docs/sponsors.md b/docs/sponsors.md
new file mode 100644
index 0000000..8cea722
--- /dev/null
+++ b/docs/sponsors.md
@@ -0,0 +1,21 @@
+---
+description: 'The people that help make Enmap, and my other projects, possible!'
+---
+
+# Supporters and Partners
+
+## Enmap Partners
+
+### An Idiot's Guide
+
+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!
+
+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 [An Idiot's Guide to Discord.js](https://anidiots.guide/) \(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 [https://anidiots.guide/](https://anidiots.guide/) for the guide itself and [https://discord.gg/vXVxsAjSMF](https://discord.gg/vXVxsAjSMF) to join us on Discord.
+
+## Enmap Supporters
+
+### AndrewJWin
+
+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.
diff --git a/docs/usage/README.md b/docs/usage/README.md
new file mode 100644
index 0000000..cbf955c
--- /dev/null
+++ b/docs/usage/README.md
@@ -0,0 +1,48 @@
+# Usage Documentation
+
+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:
+
+```javascript
+const Enmap = require("enmap");
+const myEnmap = new Enmap();
+
+// you can now use your enmap directly
+```
+
+### Persistent Enmaps
+
+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.
+
+```javascript
+const Enmap = require("enmap");
+
+// Normal enmap with default options
+const myEnmap = new Enmap({name: "points"});
+
+// non-cached, auto-fetch enmap:
+const otherEnmap = new Enmap({
+ name: "settings",
+ autoFetch: true,
+ fetchAll: false
+});
+```
+
+### Enmap Options
+
+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.
+
diff --git a/docs/usage/arrays.md b/docs/usage/arrays.md
new file mode 100644
index 0000000..ab7d910
--- /dev/null
+++ b/docs/usage/arrays.md
@@ -0,0 +1,49 @@
+# Array Methods
+
+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.
+
+```javascript
+myEnmap.set("simpleArray", [1,2,3,4,5]);
+
+myEnmap.set("arrInObj", {
+ name: "Bob",
+ aliases: ["Bobby", "Robert"]
+});
+
+
+myEnmap.set('objectarray', [{ a: 1, b: 2, c: 3 }, { d: 4, e: 5, f: 6 }]);
+```
+
+## Adding to the array
+
+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.
+
+```javascript
+myEnmap.push("simpleArray", 6);
+// now [1,2,3,4,5,6]
+
+myEnmap.push("arrInObj", "Robby", "aliases");
+// now ["Bobby", "Robert", "Robby"]
+```
+
+> The third parameter in push is the "path" to the array in an object. It works the same as the properties path used in [Working With Objects](objects.md).
+
+## Removing from 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.
+
+```javascript
+myEnmap.remove("simpleArray", 2);
+// now [1,3,4,5,6]
+
+myEnmap.remove("arrInObject", "Bobby", "aliases");
+// now ["Robert", "Robby"]
+
+myEnmap.remove('objectarray', (value) => value.e === 5);
+// value is now [{ a: 1, b: 2, c: 3 }]
+```
+
diff --git a/docs/usage/basic.md b/docs/usage/basic.md
new file mode 100644
index 0000000..bf25a5d
--- /dev/null
+++ b/docs/usage/basic.md
@@ -0,0 +1,60 @@
+# Basic Data Use
+
+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`
+
+## Writing Data
+
+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.
+
+There is however a workaround, which is to use [Serializing and Deserializing](serialize.md).
+
+> Objects and Arrays are a little more complex to deal with, so they have their own page. See [Working with Objects](objects.md) for more information.
+
+The usage for the `set()` method is simple:
+
+```text
+.set(key, value);
+```
+
+* `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:
+
+```javascript
+myEnmap.set('boolean', true);
+myEnmap.set('integer', 42);
+myEnmap.set('someFloat', 73.2345871);
+myEnmap.set("Test2", "test2");
+```
+
+## Retrieving Data
+
+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!
+
+```javascript
+const floatValue = myEnmap.get('someFloat');
+const test = myEnmap.get('Test2');
+
+// you can even use booleans in conditions:
+if(myEnmap.get('boolean')) {
+ // boolean is true!
+}
+```
+
+That's pretty much it for only retrieving a single data value. There are more complex operations that are available, take a look at [Array Methods](arrays.md) for the more advanced things you can do on Enmap's data!
+
+### Deleting Data
+
+Removing data from Enmap is as simple as saving or retrieving. You can easily use the delete\(\) method as such:
+
+```javascript
+myEnmap.delete("integer");
+
+myEnmap.delete("boolean");
+```
+
diff --git a/docs/usage/math.md b/docs/usage/math.md
new file mode 100644
index 0000000..4b890a0
--- /dev/null
+++ b/docs/usage/math.md
@@ -0,0 +1,54 @@
+# Mathematical Methods
+
+{% hint style="warning" %}
+This page is a work in progress and may not have the polish of a usual Evie-Written document!
+{% endhint %}
+
+Some quick docs:
+
+### enmap.math\(key, operation, operator, \[objectPath\]\)
+
+```javascript
+// Assuming
+points.set("number", 42);
+points.set("numberInObject", {sub: { anInt: 5 }});
+
+points.math("number", "/", 2); // 21
+points.math("number", "add", 5); // 26
+points.math("number", "modulo", 3); // 2
+points.math("numberInObject", "+", 10, "sub.anInt");
+```
+
+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.
+
+### enmap.inc\(key, \[objectPath\]\)
+
+```javascript
+// Assuming
+points.set("number", 42);
+points.set("numberInObject", {sub: { anInt: 5 }});
+
+points.inc("number"); // 43
+points.inc("numberInObject", "sub.anInt"); // {sub: { anInt: 6 }}
+```
+
+### enmap.dec\(key. \[objectPath\]\)
+
+```javascript
+// Assuming
+points.set("number", 42);
+points.set("numberInObject", {sub: { anInt: 5 }});
+
+points.dec("number"); // 41
+points.dec("numberInObject", "sub.anInt"); // {sub: { anInt: 4 }}
+```
+
+
+
diff --git a/docs/usage/objects.md b/docs/usage/objects.md
new file mode 100644
index 0000000..a7a9a76
--- /dev/null
+++ b/docs/usage/objects.md
@@ -0,0 +1,79 @@
+# Working with Objects
+
+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:
+
+```javascript
+const myStructure = {
+ first: "blah",
+ second: "foo",
+ changeme: "initial",
+ isCool: false
+ sub: {
+ yay: true,
+ thing: "amagig"
+ }
+}
+```
+
+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:
+
+```javascript
+myEnmap.set("someObject", myStructure);
+
+// Or directly the object
+myEnmap.set("someObject", {first: "blah", ...});
+
+// Works with arrays, too!
+myEnmap.set("someArray", ["one", "two", "three"]);
+```
+
+> **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.
+
+## Getting properties
+
+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 `.get(key, path)`.
+
+```javascript
+const second = myEnmap.get("someObject", "second");
+// returns "foo"
+
+const thing = myEnmap.get("someObject", "sub.yay");
+// returns true
+
+// The path can be dynamic, too:
+const propToGet = "thing";
+const blah = myEnmap.get("someObject", `sub.${propToGet}`);
+```
+
+## Checking if a property exists
+
+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:
+
+```javascript
+myEnmap.has("someObject", "sub.thing"); // returns true
+
+myEnmap.has("someObject", "heck"); // returns false.
+```
+
+## Modifying Properties
+
+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:
+
+```javascript
+// Set an object property
+myEnmap.set("someObject", "newThing", "sub.blah");
+
+// Set an array property
+myEnmap.set("someArray", "four", 3);
+```
+
+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](arrays.md).
+
diff --git a/docs/usage/paths.md b/docs/usage/paths.md
new file mode 100644
index 0000000..0743488
--- /dev/null
+++ b/docs/usage/paths.md
@@ -0,0 +1,53 @@
+---
+description: 'What is Paths in Enmap, how to use them, what is their syntax?'
+---
+
+# Understanding Paths
+
+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.
+
+```javascript
+const myObject = {
+ a: "foo",
+ b: true,
+ c: {
+ but: "who",
+ are: "you?",
+ and: ["are you", "you?"],
+ },
+ sub: { values: { are: { "cool" } } },
+};
+```
+
+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.
+
+### Doing it in Enmap
+
+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:
+
+```javascript
+myEnmap.set("myObject", {
+ a: "foo",
+ b: true,
+ c: {
+ but: "who",
+ are: "you?",
+ and: ["are you", "you?"],
+ },
+ sub: { values: { are: { "cool" } } },
+});
+```
+
+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.
+
diff --git a/docs/usage/serialize.md b/docs/usage/serialize.md
new file mode 100644
index 0000000..23aed2d
--- /dev/null
+++ b/docs/usage/serialize.md
@@ -0,0 +1,102 @@
+---
+description: >-
+ 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.
+---
+
+# Serializing and Deserializing
+
+_**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.
+
+### What are they?
+
+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.
+
+```javascript
+// the default serializer
+const serializer = (data, key) => {
+ return data;
+};
+```
+
+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.
+
+```javascript
+// the default deserializer
+const deserializer = (data, key) => {
+ return data;
+};
+```
+
+### Examples
+
+#### Guild Settings: A more sensible example
+
+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.
+
+```javascript
+// Imagine the client and stuff is already defined.
+
+
+// The function that runs when storing data
+const serializeData: data => {
+ return {
+ ...data,
+ // stores the guild as ID
+ guild: guild.id,
+ // stores the user as ID
+ user: user.id,
+ }
+};
+
+// This one runs when loading.
+const deserializeData: data => {
+ return {
+ ...data,
+ // gets the guild itself from the cache from its ID
+ guild: client.guilds.cache.get(data.guild),
+ // Same with the user!
+ user: client.users.cache.get(data.user),
+ }
+};
+
+// Default Settings can no longer store defaults for roles and channels.
+const defaultSettings = {
+ prefix: "!",
+ modLogChannel: null,
+ modRole: null,
+ adminRole: null,
+ welcomeChannel: null,
+ welcomeMessage: "Say hello to {{user}}, everyone!"
+}
+
+// Our enmap has shiny new options here!
+client.settings = new Enmap({
+ name: "settings",
+ cloneLevel: 'deep',
+ serializer: serializeData,
+ deserializer: deserializeData,
+ // Might as well autoensure, eh?
+ autoEnsure: defaultSettings,
+});
+
+
+// Store some data, obviously needs to be run in the right place:
+client.settings.set(message.guild.id,
+ message.mentions.channels.first(),
+ 'welcomeChannel'
+);
+
+client.settings.set(message.guild.id,
+ message.mentions.roles.first(),
+ 'adminRole'
+);
+
+// GET the data after
+const welcomeChannel = client.settings.get(message.guild.id, 'welcomeChannel');
+welcomeChannel.send("This works without having to find or get the channel!");
+```
+
diff --git a/docs/usage/using-enmap.multi.md b/docs/usage/using-enmap.multi.md
new file mode 100644
index 0000000..f1f3b37
--- /dev/null
+++ b/docs/usage/using-enmap.multi.md
@@ -0,0 +1,20 @@
+# Using Enmap.multi
+
+To account for people that might use a large number of enmaps in the same project, I've created a new \`multi\(\)\` method that can be used to instanciate multiple peristent enmaps together.
+
+The method takes 3 arguments:
+
+* An `array` of names for the enmaps to be created.
+* A Provider \(not instanciated\), from any of the available ones.
+* An `options` object containing any of the options needed to instanciate the provider. Do not add `name` to this, as it will use the names in the array instead.
+
+Below, an example that uses destructuring:
+
+```javascript
+const Enmap = require('enmap');
+const Provider = require('enmap-mongo');
+const { settings, tags, blacklist, langs } =
+ Enmap.multi(['settings', 'tags', 'blacklist', 'langs'],
+ Provider, { url: "mongodb://localhost:27017/enmap" });
+```
+
diff --git a/docs/usage/using-from-multiple-files.md b/docs/usage/using-from-multiple-files.md
new file mode 100644
index 0000000..d3ba0f4
--- /dev/null
+++ b/docs/usage/using-from-multiple-files.md
@@ -0,0 +1,110 @@
+---
+description: >-
+ 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.
+---
+
+# Using from multiple files
+
+## A common issue
+
+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?
+
+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 _[_instance _](https://js.evie.dev/classes)_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.
+
+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?
+
+## The Shared Variable Method
+
+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:
+
+```javascript
+const Discord = require("discord.js");
+const client = new Discord.Client();
+
+const Enmap = require("enmap");
+
+// this is the important bit
+client.settings = new Enmap({ name: "settings" });
+client.tags = new Enmap({ name: "tags" });
+
+// your normal events here
+client.on("message", message => {
+ const guildSettings = client.settings.get(message.guild.id);
+ // works here
+});
+
+client.login(token);
+```
+
+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.
+
+{% hint style="danger" %}
+Important Note: Do NOT override Discord.js' existing collections! That means, client.users, client.guilds, etc. [See all the properties and methods for the Discord.js client](https://discord.js.org/#/docs/main/stable/class/Client) - none of these should be overridden.
+{% endhint %}
+
+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.
+
+## The Module Method
+
+All things considered, [modules ](https://js.evie.dev/modules)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 [My JavaScript Guide](https://js.evie.dev/modules), modules are fairly straightforward. This is how I have done an Enmap shared module before:
+
+```javascript
+const Enmap = require("enmap");
+
+module.exports = {
+ settings: new Enmap({
+ name: "settings",
+ autoFetch: true,
+ fetchAll: false
+ }),
+ users: new Enmap("users"),
+ tags: new Emmap({ name : "tags" })
+}
+```
+
+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:
+
+```javascript
+const db = require("./db.js");
+
+console.log(db.settings.size);
+db.tags.set("blah", {
+ guild: "1234",
+ author: "4231",
+ name: "blah",
+ content: "I'm bored, mommy!"
+});
+```
+
+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:
+
+```javascript
+const guildTags = db.tags.find(tag => tag.guild === message.guild.id);
+```
+
+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:
+
+```javascript
+const Enmap = require("enmap");
+
+module.exports = {
+ settings: new Enmap({
+ name: "settings",
+ autoFetch: true,
+ fetchAll: false
+ }),
+ users: new Enmap("users"),
+ tags: new Emmap({ name : "tags" }),
+ getTags: (guild) => {
+ return this.tags.find(tag => tag.guild === message.guild.id);
+ }
+}
+```
+
+And there you have it! There are other ways to build the exports, you can also split it differently, take a look at [My Modules Guide ](https://js.evie.dev/modules)for more information.
diff --git a/jest.config.js b/jest.config.js
index 453bc80..13eaf89 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,4 +1,4 @@
-module.exports = {
+export default {
coverageDirectory: 'out/coverage',
testEnvironment: 'node',
testMatch: [
diff --git a/package-lock.json b/package-lock.json
index 310a1c3..d6eddf4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,25 +1,26 @@
{
"name": "enmap",
- "version": "5.9.10",
+ "version": "6.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "enmap",
- "version": "5.9.10",
+ "version": "6.0.0",
"license": "Apache-2.0",
"dependencies": {
+ "better-serialize": "^1.0.0",
"better-sqlite3": "^9.4.3",
- "lodash": "^4.17.21",
- "on-change": "^3.0.2",
- "serialize-javascript": "^6.0.2"
+ "lodash-es": "^4.17.21",
+ "on-change": "^5.0.1"
},
"devDependencies": {
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",
"jest": "^26.6.3",
"jsdoc-to-markdown": "^8.0.0",
- "limax": "^4.0.0"
+ "limax": "^4.0.0",
+ "retypeapp": "^3.5.0"
}
},
"node_modules/@ampproject/remapping": {
@@ -1981,6 +1982,15 @@
}
]
},
+ "node_modules/better-serialize": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/better-serialize/-/better-serialize-1.0.0.tgz",
+ "integrity": "sha512-qNUKbTp1b6z0w7xdFIjs6duDWkNqukX87nsBwWwTcSl48QGY6Uy2xVY/Hd0ovd0rmABAt9cvD5sDFMQsBt7oaw==",
+ "engines": {
+ "node": ">=16",
+ "npm": ">=6"
+ }
+ },
"node_modules/better-sqlite3": {
"version": "9.4.3",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-9.4.3.tgz",
@@ -5181,7 +5191,13 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
- "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.camelcase": {
"version": "4.3.0",
@@ -5685,11 +5701,11 @@
}
},
"node_modules/on-change": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/on-change/-/on-change-3.0.2.tgz",
- "integrity": "sha512-rdt5YfIfo86aFNwvQqzzHMpaPPyVQ/XjcGK01d46chZh47G8Xzvoao79SgFb03GZfxRGREzNQVJuo31drqyIlA==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/on-change/-/on-change-5.0.1.tgz",
+ "integrity": "sha512-n7THCP7RkyReRSLkJb8kUWoNsxUIBxTkIp3JKno+sEz6o/9AJ3w3P9fzQkITEkMwyTKJjZciF3v/pVoouxZZMg==",
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sindresorhus/on-change?sponsor=1"
@@ -6004,13 +6020,6 @@
}
]
},
- "node_modules/randombytes": {
- "version": "2.1.0",
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "^5.1.0"
- }
- },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -6320,6 +6329,15 @@
"node": ">=0.12"
}
},
+ "node_modules/retypeapp": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/retypeapp/-/retypeapp-3.5.0.tgz",
+ "integrity": "sha512-YhHilXfKLXU2BCZ0lfj4tUVMrk8nUjkSCAoV52xIPfRfSKQQah9D5vn2Eiu+FUmeGGEB9gykr8UVi7CvjmiqbQ==",
+ "dev": true,
+ "bin": {
+ "retype": "retype.js"
+ }
+ },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -6580,14 +6598,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/serialize-javascript": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
- "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
- "dependencies": {
- "randombytes": "^2.1.0"
- }
- },
"node_modules/set-blocking": {
"version": "2.0.0",
"dev": true,
diff --git a/package.json b/package.json
index d235b81..e2bf4c2 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,13 @@
{
"name": "enmap",
- "version": "5.9.10",
+ "version": "6.0.0",
"description": "A simple database wrapper to make sqlite database interactions much easier for beginners, with additional array helper methods.",
"main": "src/index.js",
+ "type": "module",
"scripts": {
- "test": "jest",
- "docs": "node ./docs/build.js"
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
+ "build-api-docs": "node ./scripts/build-docs.js",
+ "build-docs": "cd docs && npx retype build"
},
"repository": {
"type": "git",
@@ -32,17 +34,18 @@
"author": "Evelyne Lachance (https://evie.codes/)",
"license": "Apache-2.0",
"dependencies": {
+ "better-serialize": "^1.0.0",
"better-sqlite3": "^9.4.3",
- "lodash": "^4.17.21",
- "on-change": "^3.0.2",
- "serialize-javascript": "^6.0.2"
+ "lodash-es": "^4.17.21",
+ "on-change": "^5.0.1"
},
"devDependencies": {
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",
"jest": "^26.6.3",
"jsdoc-to-markdown": "^8.0.0",
- "limax": "^4.0.0"
+ "limax": "^4.0.0",
+ "retypeapp": "^3.5.0"
},
"types": "./typings/index.d.ts"
}
diff --git a/retype.manifest b/retype.manifest
new file mode 100644
index 0000000..633541f
--- /dev/null
+++ b/retype.manifest
@@ -0,0 +1,153 @@
+{
+ "version": "3.5.0",
+ "created": "2024-04-08T05:24:23Z",
+ "files": [
+ {
+ "path": ".nojekyll"
+ },
+ {
+ "path": "404.html"
+ },
+ {
+ "path": "CNAME"
+ },
+ {
+ "path": "docs\\api\\index.html"
+ },
+ {
+ "path": "docs\\assets\\enmap-logo.svg"
+ },
+ {
+ "path": "docs\\assets\\favicon.ico"
+ },
+ {
+ "path": "docs\\assets\\favicon.png"
+ },
+ {
+ "path": "docs\\blog-posts\\enmaps-history\\index.html"
+ },
+ {
+ "path": "docs\\blog-posts\\index.html"
+ },
+ {
+ "path": "docs\\blog-posts\\josh\\index.html"
+ },
+ {
+ "path": "docs\\blog-posts\\why-remove-cache\\index.html"
+ },
+ {
+ "path": "docs\\blog-posts\\why-sqlite-only\\index.html"
+ },
+ {
+ "path": "docs\\help\\cjstoesm\\index.html"
+ },
+ {
+ "path": "docs\\index.html"
+ },
+ {
+ "path": "docs\\install\\index.html"
+ },
+ {
+ "path": "docs\\install\\troubleshooting-guide\\index.html"
+ },
+ {
+ "path": "docs\\install\\upgrade\\index.html"
+ },
+ {
+ "path": "docs\\install\\upgradev6\\index.html"
+ },
+ {
+ "path": "docs\\sponsors\\index.html"
+ },
+ {
+ "path": "docs\\summary\\index.html"
+ },
+ {
+ "path": "docs\\usage\\arrays\\index.html"
+ },
+ {
+ "path": "docs\\usage\\basic\\index.html"
+ },
+ {
+ "path": "docs\\usage\\fetchall\\index.html",
+ "old": true
+ },
+ {
+ "path": "docs\\usage\\index.html"
+ },
+ {
+ "path": "docs\\usage\\init\\index.html",
+ "old": true
+ },
+ {
+ "path": "docs\\usage\\math\\index.html"
+ },
+ {
+ "path": "docs\\usage\\objects\\index.html"
+ },
+ {
+ "path": "docs\\usage\\paths\\index.html"
+ },
+ {
+ "path": "docs\\usage\\persistent-enmaps\\index.html",
+ "old": true
+ },
+ {
+ "path": "docs\\usage\\serialize\\index.html"
+ },
+ {
+ "path": "docs\\usage\\using-enmap.multi\\index.html"
+ },
+ {
+ "path": "docs\\usage\\using-from-multiple-files\\index.html"
+ },
+ {
+ "path": "index.html"
+ },
+ {
+ "path": "resources\\css\\retype.css"
+ },
+ {
+ "path": "resources\\fonts\\Inter-italic-latin-var.woff2"
+ },
+ {
+ "path": "resources\\fonts\\Inter-roman-latin-var.woff2"
+ },
+ {
+ "path": "resources\\js\\config.js"
+ },
+ {
+ "path": "resources\\js\\lunr.js"
+ },
+ {
+ "path": "resources\\js\\prism.js"
+ },
+ {
+ "path": "resources\\js\\retype.js"
+ },
+ {
+ "path": "resources\\js\\search.json"
+ },
+ {
+ "path": "resources\\lunr.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\mermaid.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\prism.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\retype.js.LICENSE.txt"
+ },
+ {
+ "path": "resources\\retype.LICENSE.txt"
+ },
+ {
+ "path": "robots.txt"
+ },
+ {
+ "path": "sitemap.xml.gz"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/retype.yml b/retype.yml
new file mode 100644
index 0000000..77b2470
--- /dev/null
+++ b/retype.yml
@@ -0,0 +1,17 @@
+input: ./docs
+output: .retype
+url: enmap.evie.dev
+branding:
+ logo: assets/enmap-logo.svg
+favicon: assets/favicon.png
+links:
+- text: Github
+ link: https://github.com/eslachance/enmap
+- text: NPM
+ link: https://npmjs.com/package/enmap
+- text: Discord
+ link: https://discord.gg/N7ZKH3P
+edit:
+ repo: "http://github.com/eslachance/enmap/edit/"
+footer:
+ copyright: "© Copyright {{ year }}. All rights reserved."
diff --git a/scripts/build-docs.js b/scripts/build-docs.js
new file mode 100644
index 0000000..59e96be
--- /dev/null
+++ b/scripts/build-docs.js
@@ -0,0 +1,68 @@
+/* eslint-disable no-useless-escape */
+import jsdoc2md from 'jsdoc-to-markdown';
+import { writeFile } from 'node:fs/promises';
+import slug from 'limax';
+
+const htmlEntities = {
+ nbsp: ' ',
+ cent: '¢',
+ pound: '£',
+ yen: '¥',
+ euro: '€',
+ copy: '©',
+ reg: '®',
+ lt: '<',
+ gt: '>',
+ quot: '"',
+ amp: '&',
+ apos: '\'',
+};
+
+const unescapeHTML = str => str.replace(/\&([^;]+);/g, (entity, entityCode) => {
+ let match;
+
+ if (entityCode in htmlEntities) {
+ return htmlEntities[entityCode];
+ /* eslint no-cond-assign: 0 */
+ } else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
+ return String.fromCharCode(parseInt(match[1], 16));
+ /* eslint no-cond-assign: 0 */
+ } else if (match = entityCode.match(/^#(\d+)$/)) {
+ return String.fromCharCode(~~match[1]);
+ } else {
+ return entity;
+ }
+});
+
+const apiDocsHeader = `---
+description: >-
+ The complete and unadultered API documentation for every single method and
+ property accessible in Enmap.
+---
+
+# Full Documentation
+
+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.
+
+{% hint style="warning" %}
+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!
+{% endhint %}
+
+
+`;
+
+const finalize = str => str
+ .replace(/\[Enmap\]\(#Enmap\)/gi, 'Enmap')
+ .replace(/\[Enmap<\/code>\]\(#Enmap\)/gi, 'Enmap
')
+ .replace('* [new Enmap(iterable, [options])](#new_Enmap_new)', '* [new Enmap(iterable, [options])](#new-enmap-iterable-options)')
+ .split('\n\n')[2].split('')[0];
+
+const regexread = /^ {8}\* \[\.(.*?)\]\((.*?)\)(.*?)(\(#.*?\)|)$/gm;
+
+const parseData = data => finalize(data.replace(regexread, (_, b, __, d) =>
+ ` * [.${b}](#${slug(`enmap.${b} ${unescapeHTML(d.replace(/<\/?code>/g, ''))}`)})${d}`));
+
+
+const rendered = await jsdoc2md.render({ files: './src/index.js' });
+await writeFile('./docs/api.md', apiDocsHeader + parseData(rendered));
+
diff --git a/src/error.js b/src/error.js
index 8fec49d..86a40d3 100644
--- a/src/error.js
+++ b/src/error.js
@@ -9,4 +9,4 @@ class CustomError extends Error {
}
-module.exports = CustomError;
+export default CustomError;
diff --git a/src/index.js b/src/index.js
index 15b3707..0034e39 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,194 +1,161 @@
-// Lodash should probably be a core lib but hey, it's useful!
-const {
- get: _get,
- set: _set,
- has: _has,
+import {
+ get as _get,
+ set as _set,
isNil,
isFunction,
isArray,
isObject,
- toPath,
- merge,
- clone,
cloneDeep,
-} = require('lodash');
-const serialize = require('serialize-javascript');
-const onChange = require('on-change');
+ merge,
+} from 'lodash-es';
+import { stringify, parse } from 'better-serialize';
+import onChange from 'on-change';
// Custom error codes with stack support.
-const Err = require('./error.js');
+import Err from './error.js';
// Native imports
-const { resolve, sep } = require('path');
-const fs = require('fs');
+import { existsSync, readFileSync, mkdirSync } from 'fs';
+import { resolve, sep } from 'path';
// Package.json
-const pkgdata = require('../package.json');
-const Database = require('better-sqlite3/lib/database');
+const pkgdata = JSON.parse(readFileSync('./package.json', 'utf8'));
-const instances = [];
+import Database from 'better-sqlite3';
-process.on('exit', () => {
- for (const instance of instances) instance.close();
-});
+const NAME_REGEX = /^([\w-]+)$/;
/**
- * A enhanced Map structure with additional utility methods.
- * Can be made persistent
- * @extends {Map}
+ * A simple, synchronous, fast key/value storage build around better-sqlite3.
+ * Contains extra utility methods for managing arrays and objects.
+ * @class Enmap
*/
-class Enmap extends Map {
- #cloneLevel;
+class Enmap {
+ #name;
+ #db;
+ #inMemory;
+ #autoEnsure;
#ensureProps;
#serializer;
#deserializer;
- #isDestroyed;
- #name;
- #off;
- #fetchAll;
- #autoFetch;
- #autoEnsure;
- #wal;
- #polling;
- #pollingInterval;
- #verbose;
- #db;
- #lastSync;
+ #changedCB;
+
/**
* Initializes a new Enmap, with options.
- * @param {Iterable|string|void} iterable 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.
- * @param {Object} [options] Additional options for the enmap. See https://enmap.evie.codes/usage#enmap-options for details.
- * @param {string} [options.name] 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).
- * @param {boolean} [options.fetchAll] Defaults to `true`. When enabled, will automatically fetch any key that's requested using get,
- * or other retrieval methods. This is a "synchronous" operation, which means it doesn't need any of this promise or callback use.
+ * @param {Object} [options] Options for the enmap. See https://enmap.evie.codes/usage#enmap-options for details.
+
+ * @param {string} [options.name] The name of the enmap. Represents its table name in sqlite. Unless inMemory is set to true, the enmap will be persisted to disk.
+
* @param {string} [options.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!
- * *Note*: Will not automatically create the folder if set manually, so make sure it exists.
- * @param {string} [options.cloneLevel] 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.
- * @param {boolean} [options.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.
- * @param {number} [options.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.
+ * *Note*: Enmap will not automatically create the folder if it is set manually, so make sure it exists before starting your code!
+
* @param {boolean} [options.ensureProps] 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.
+
* @param {*} [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.
- * @param {boolean} [options.autoFetch] 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.
+
* @param {Function} [options.serializer] 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).
+
* @param {Function} [options.deserializer] 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).
- * @param {boolean} [options.wal=false] Check out Write-Ahead Logging: https://www.sqlite.org/wal.html
- * @param {Function} [options.verbose=(query) => null] A function to call with the direct SQL statement being ran by Enmap internally
+ *
+ * @param {boolean} [options.inMemory] Optional. If set to true, the enmap will be in-memory only, and will not write to disk. Useful for temporary stores.
+ *
+ * @param {Object} [options.sqliteOptions] Optional. An object of options to pass to the better-sqlite3 Database constructor.
* @example
* const Enmap = require("enmap");
- * // Non-persistent enmap:
- * const inMemory = new Enmap();
- *
- * // Named, Persistent enmap with string option
- * const myEnmap = new Enmap("testing");
- *
- * // Enmap that does not fetch everything, but does so on per-query basis:
- * const myEnmap = new Enmap({name: "testing", fetchAll: false});
+
+ * // Named, Persistent enmap
+ * const myEnmap = new Enmap({ name: "testing" });
+ *
+ * // Memory-only enmap
+ * const memoryEnmap = new Enmap({ inMemory: true });
*
* // Enmap that automatically assigns a default object when getting or setting anything.
* const autoEnmap = new Enmap({name: "settings", autoEnsure: { setting1: false, message: "default message"}})
*/
- constructor(iterable, options = {}) {
- if (typeof iterable === 'string') {
- options.name = iterable;
- iterable = null;
- }
- if (!iterable || !(Symbol.iterator in iterable)) {
- //@ts-ignore
- options = iterable || options;
- iterable = null;
+ constructor(options) {
+ this.#inMemory = options.inMemory ?? false;
+ if (options.name === '::memory::') {
+ this.#inMemory = true;
+ console.warn(
+ 'Using ::memory:: as a name is deprecated and will be removed in the future. Use { inMemory: true } instead.',
+ );
}
- super();
-
- // Define local properties from the options.
- this.#off = Symbol('option_off');
- this.#name = options.name ?? '::memory::';
- this.#fetchAll = options.fetchAll ?? true;
- this.#autoFetch = options.autoFetch ?? true;
- this.#autoEnsure = options.autoEnsure ?? this.#off;
- this.#wal = options.wal ?? true;
- this.#polling = options.polling ?? false;
- this.#pollingInterval = options.pollingInterval ?? 1000;
this.#ensureProps = options.ensureProps ?? true;
this.#serializer = options.serializer ? options.serializer : (data) => data;
- this.#verbose = options.verbose ? options.verbose : () => null;
this.#deserializer = options.deserializer
? options.deserializer
: (data) => data;
+ this.#autoEnsure = options.autoEnsure;
- // CloneLevel is a little more involved... This'll be easier with TS :P
- this.#cloneLevel = options.cloneLevel ?? 'deep';
- if (!['none', 'shallow', 'deep'].includes(this.#cloneLevel)) {
- throw new Err(
- 'Unknown Clone Level. Options are none, shallow, deep. Default is deep.',
- 'EnmapOptionsError',
- );
- }
-
- if (this.#name !== '::memory::') {
- // Define the data directory where the enmap is stored.
+ if (this.#inMemory) {
+ this.#db = new Database(':memory:');
+ this.#name = 'MemoryEnmap';
+ } else {
+ this.#name = options.name;
if (!options.dataDir) {
- if (!fs.existsSync('./data')) {
- fs.mkdirSync('./data');
+ if (!existsSync('./data')) {
+ mkdirSync('./data');
}
}
-
const dataDir = resolve(process.cwd(), options.dataDir || 'data');
- this.#db = new Database(`${dataDir}${sep}enmap.sqlite`, {
- verbose: this.#verbose,
- });
- } else {
- this.#db = new Database(':memory:', { verbose: this.#verbose });
- this.#name = 'MemoryEnmap';
+ this.#db = new Database(
+ `${dataDir}${sep}enmap.sqlite`,
+ options.sqliteOptions,
+ );
}
- if (this.#polling) {
- process.emitWarning(
- 'Polling features will be removed in Enmap v6. If you need enmap in multiple processes, please consider moving to JOSH, https://josh.evie.dev/',
- );
+ if (!this.#db) {
+ throw new Err('Database Could Not Be Opened', 'EnmapDBConnectionError');
}
- // Initialize this property, to prepare for a possible destroy() call.
- // This is completely ignored in all situations except destroying the enmap.
- this.#validateName();
- this.#isDestroyed = false;
- this.#init(this.#db);
- instances.push(this);
+ // Check if enmap by this name is in the sqlite master table
+ const table = this.#db
+ .prepare(
+ "SELECT count(*) FROM sqlite_master WHERE type='table' AND name = ?;",
+ )
+ .get(this.#name);
+
+ // This is a first init, create everything!
+ if (!table['count(*)']) {
+ // Create base table
+ this.#db
+ .prepare(
+ `CREATE TABLE ${this.#name} (key text PRIMARY KEY, value text)`,
+ )
+ .run();
+
+ // Define table properties : sync and write-ahead-log
+ this.#db.pragma('synchronous = 1');
+ this.#db.pragma('journal_mode = wal');
- if (iterable) {
- for (const [key, value] of iterable) {
- this.#internalSet(key, value);
- }
+ // Create autonum table
+ this.#db
+ .prepare(
+ `CREATE TABLE IF NOT EXISTS 'internal::autonum' (enmap TEXT PRIMARY KEY, lastnum INTEGER)`,
+ )
+ .run();
}
- }
- // Left for backwards compatibility
- get defer() {
- return Promise.resolve();
+ process.on('exit', () => {
+ this.#db.close();
+ });
}
/**
- * Sets a value in Enmap.
- * @param {string} key Required. The key of the element to add to The Enmap.
- * @param {*} val Required. The value of the element to add to The Enmap.
- * If the Enmap is persistent this value MUST be stringifiable as JSON.
+ * Sets a value in Enmap. If the key already has a value, overwrites the data (or the value in a path, if provided).
+ * @param {string} key Required. The location in which the data should be saved.
+ * @param {*} value Required. The value to write.
+ * Values must be serializable, which is done through (better-serialize)[https://github.com/RealShadowNova/better-serialize]
+ * If the value is not directly serializable, please use a custom serializer/deserializer.
* @param {string} path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* // Direct Value Examples
* enmap.set('simplevalue', 'this is a string');
@@ -200,35 +167,21 @@ class Enmap extends Map {
* // Settings Properties
* enmap.set('IhazObjects', 'blue', 'color'); //modified previous object
* enmap.set('ArraysToo', 'three', 2); // changes "tree" to "three" in array.
- * @returns {Enmap} The enmap.
*/
- //@ts-ignore
- set(key, val, path = null) {
- if (isNil(key) || key.constructor.name !== 'String') {
- throw new Err(
- `Enmap requires keys to be a string. Provided: ${
- isNil(key) ? 'nil' : key.constructor.name
- }`,
- 'EnmapKeyTypeError',
- );
- }
- key = key.toString();
+ set(key, value, path) {
+ this.#keycheck(key);
let data = this.get(key);
- const oldValue = super.has(key) ? this.#clone(data) : null;
+ const oldValue = cloneDeep(data);
if (!isNil(path)) {
if (isNil(data)) data = {};
- _set(data, path, val);
+ _set(data, path, value);
} else {
- data = val;
+ data = value;
}
- if (isFunction(this.changedCB)) {
- this.changedCB(key, oldValue, data);
- }
- this.#internalSet(key, data, false);
- return super.set(key, this.#clone(data));
+ if (isFunction(this.#changedCB)) this.#changedCB(key, oldValue, data);
+ this.#set(key, data);
}
- // eslint-disable-next-line valid-jsdoc
/**
* 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.
@@ -253,49 +206,50 @@ class Enmap extends Map {
* }));
* // this example takes heavy advantage of the spread operators.
* // More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
+ * @returns {*} The modified (merged) value.
*/
update(key, valueOrFunction) {
- this.#readyCheck();
- if (isNil(key)) {
- throw new Err('Key not provided for update function', 'EnmapKeyError');
- }
+ this.#keycheck(key);
this.#check(key, ['Object']);
- this.#fetchCheck(key);
- const previousValue = this.get(key);
+ const data = this.get(key);
const fn = isFunction(valueOrFunction)
? valueOrFunction
- : () => merge(previousValue, valueOrFunction);
- const merged = fn(previousValue);
- this.#internalSet(key, merged);
+ : () => merge(data, valueOrFunction);
+ const merged = fn(data);
+ this.#set(key, merged);
return merged;
}
/**
- * Retrieves a key from the enmap.
+ * Retrieves a value from the enmap, using its key.
* @param {string} key The key to retrieve from the enmap.
* @param {string} path Optional. The property to retrieve from the object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* const myKeyValue = enmap.get("myKey");
* console.log(myKeyValue);
*
* const someSubValue = enmap.get("anObjectKey", "someprop.someOtherSubProp");
- * @return {*} The value for this key.
+ * @return {*} The parsed value for this key.
*/
- get(key, path = null) {
- this.#readyCheck();
- if (isNil(key)) return null;
- this.#fetchCheck(key);
- key = key.toString();
- if (this.#autoEnsure !== this.#off && !this.has(key)) {
- this.#internalSet(key, this.#autoEnsure);
+ get(key, path) {
+ this.#keycheck(key);
+
+ if (!isNil(this.#autoEnsure) && !this.has(key)) {
+ this.#set(key, this.#autoEnsure);
}
- const data = super.get(key);
- if (!isNil(path)) {
- this.#check(key, ['Object', 'Array']);
- return _get(data, path);
+
+ const data = this.#db
+ .prepare(`SELECT value FROM ${this.#name} WHERE key = ?`)
+ .get(key);
+ const parsed = data ? this.#parse(data.value) : null;
+ if (isNil(parsed)) return null;
+
+ if (path) {
+ this.#check(key, ['Object']);
+ return _get(parsed, path);
}
- return this.#clone(data);
+ return parsed;
}
/**
@@ -306,7 +260,7 @@ class Enmap extends Map {
* @param {string} path Optional. The property to retrieve from the object or array.
* @return {*} The value for this key.
*/
- observe(key, path = null) {
+ observe(key, path) {
this.#check(key, ['Object', 'Array'], path);
const data = this.get(key, path);
const proxy = onChange(data, () => {
@@ -316,89 +270,73 @@ class Enmap extends Map {
}
/**
- * Retrieves the number of rows in the database for this enmap, even if they aren't fetched.
- * @return {number} The number of rows in the database.
+ * Get the number of key/value pairs saved in the enmap.
+ * @readonly
+ * @returns {number} The number of elements in the enmap.
*/
- get count() {
+ get size() {
const data = this.#db
.prepare(`SELECT count(*) FROM '${this.#name}';`)
.get();
return data['count(*)'];
}
-
- /**
- * Retrieves all the indexes (keys) in the database for this enmap, even if they aren't fetched.
- * @return {Array} Array of all indexes (keys) in the enmap, cached or not.
- */
- get indexes() {
- const rows = this.#db.prepare(`SELECT key FROM '${this.#name}';`).all();
- return rows.map((row) => row.key);
+ // Aliases are cheap, why not?
+ get count() {
+ return this.size;
+ }
+ get length() {
+ return this.size;
}
/**
- * Get the better-sqlite3 database object. Useful if you want to directly query or interact with the
- * underlying SQLite database. Use at your own risk, as errors here might cause loss of data or corruption!
- * @return {Database}
+ * Get all the keys of the enmap as an array.
+ * @returns {Array} An array of all the keys in the enmap.
*/
- get db() {
- return this.#db;
+ keys() {
+ const stmt = this.#db.prepare(`SELECT key FROM ${this.#name}`);
+ const indexes = [];
+ for (const row of stmt.iterate()) {
+ indexes.push(row.key);
+ }
+ return indexes;
+ }
+ indexes() {
+ return this.keys();
}
/**
- * Fetches every key from the persistent enmap and loads them into the current enmap value.
- * @return {Enmap} The enmap containing all values.
+ * Get all the values of the enmap as an array.
+ * @returns {Array<*>} An array of all the values in the enmap.
*/
- fetchEverything() {
- this.#readyCheck();
- const rows = this.#db.prepare(`SELECT * FROM ${this.#name};`).all();
- for (const row of rows) {
- const val = this.#parseData(row.value, row.key);
- super.set(row.key, val);
+ values() {
+ const stmt = this.#db.prepare(`SELECT value FROM ${this.#name}`);
+ const values = [];
+ for (const row of stmt.iterate()) {
+ values.push(this.#parse(row.value));
}
- return this;
+ return values;
}
/**
- * Force fetch one or more key values from the enmap. If the database has changed, that new value is used.
- * @param {string|number|Array} keyOrKeys A single key or array of keys to force fetch from the enmap database.
- * @return {Enmap|*} The Enmap, including the new fetched values, or the value in case the function argument is a single key.
+ * Get all entries of the enmap as an array, with each item containing the key and value.
+ * @returns {Array>} An array of arrays, with each sub-array containing two items, the key and the value.
*/
- fetch(keyOrKeys) {
- this.#readyCheck();
- if (isArray(keyOrKeys)) {
- const data = this.#db
- .prepare(
- `SELECT * FROM ${this.#name} WHERE key IN (${'?, '
- .repeat(keyOrKeys.length)
- .slice(0, -2)})`,
- )
- .all(keyOrKeys);
- for (const row of data) {
- super.set(row.key, this.#parseData(row.value, row.key));
- }
- return this;
- } else {
- const data = this.#db
- .prepare(`SELECT * FROM ${this.#name} WHERE key = ?;`)
- .get(keyOrKeys);
- if (!data) return null;
- super.set(keyOrKeys, this.#parseData(data.value, keyOrKeys));
- return this.#parseData(data.value, keyOrKeys);
+ entries() {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ const entries = [];
+ for (const row of stmt.iterate()) {
+ entries.push([row.key, this.#parse(row.value)]);
}
+ return entries;
}
/**
- * Removes a key or keys from the cache - useful when disabling autoFetch.
- * @param {string|number|Array} keyOrArrayOfKeys A single key or array of keys to remove from the cache.
- * @returns {Enmap} The enmap minus the evicted keys.
+ * Get the better-sqlite3 database object. Useful if you want to directly query or interact with the
+ * underlying SQLite database. Use at your own risk, as errors here might cause loss of data or corruption!
+ * @return {Database}
*/
- evict(keyOrArrayOfKeys) {
- if (isArray(keyOrArrayOfKeys)) {
- keyOrArrayOfKeys.forEach((key) => super.delete(key));
- } else {
- super.delete(keyOrArrayOfKeys);
- }
- return this;
+ get db() {
+ return this.#db;
}
/**
@@ -406,6 +344,7 @@ class Enmap extends Map {
* 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.
+ * @readonly
* @example
* enmap.set(enmap.autonum, "This is a new value");
* @return {number} The generated key number.
@@ -423,39 +362,12 @@ class Enmap extends Map {
return lastnum.toString();
}
- /**
- * 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
- * enmap.changed((keyName, oldValue, newValue) => {
- * console.log(`Value of ${keyName} has changed from: \n${oldValue}\nto\n${newValue}`);
- * });
- * @param {Function} cb A callback function that will be called whenever data changes in the enmap.
- */
- changed(cb) {
- this.changedCB = cb;
- }
-
- /**
- * Shuts down the database. USING THIS MAKES THE ENMAP UNUSABLE. You should
- * only use this method if you are closing your entire application.
- * This is already done by Enmap automatically on shutdown unless you disabled it.
- * @returns {Enmap} The enmap.
- */
- close() {
- this.#readyCheck();
- instances.splice(instances.indexOf(this), 1);
- this.#db.close();
- return this;
- }
-
/**
* Push to an array value in Enmap.
* @param {string} key Required. The key of the array element to push to in Enmap.
- * This value MUST be a string or number.
- * @param {*} val Required. The value to push to the array.
+ * @param {*} value Required. The value to push to the array.
* @param {string} path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @param {boolean} allowDupes Optional. Allow duplicate values in the array (default: false).
* @example
* // Assuming
@@ -464,21 +376,16 @@ class Enmap extends Map {
*
* enmap.push("simpleArray", 5); // adds 5 at the end of the array
* enmap.push("arrayInObject", "five", "sub"); // adds "five" at the end of the sub array
- * @returns {Enmap} The enmap.
*/
- push(key, val, path = null, allowDupes = false) {
- const data = this.get(key);
- this.#check(key, 'Array', path);
- if (!isNil(path)) {
- const propValue = _get(data, path);
- if (!allowDupes && propValue.indexOf(val) > -1) return this;
- propValue.push(val);
- _set(data, path, propValue);
- } else {
- if (!allowDupes && data.indexOf(val) > -1) return this;
- data.push(val);
- }
- return this.#internalSet(key, data);
+ push(key, value, path, allowDupes = false) {
+ this.#keycheck(key);
+ this.#check(key, ['Array', 'Object']);
+ const data = this.get(key, path);
+ if (!isArray(data))
+ throw new Err('Key does not point to an array', 'EnmapPathError');
+ if (!allowDupes && data.includes(value)) return;
+ data.push(value);
+ this.set(key, data);
}
// AWESOME MATHEMATICAL METHODS
@@ -499,13 +406,15 @@ class Enmap extends Map {
* points.math("number", "add", 5); // 26
* points.math("number", "modulo", 3); // 2
* points.math("numberInObject", "+", 10, "sub.anInt");
- *
- * @returns {Enmap} The enmap.
+ * @returns {number} The updated value after the operation
*/
- math(key, operation, operand, path = null) {
- this.#check(key, 'Number', path);
- const data = this.get(key, path);
- return this.set(key, this.#mathop(data, operation, operand), path);
+ math(key, operation, operand) {
+ this.#keycheck(key);
+ this.#check(key, ['Number']);
+ const data = this.get(key);
+ const updatedValue = this.#math(data, operation, operand);
+ this.set(key, updatedValue);
+ return updatedValue;
}
/**
@@ -519,19 +428,14 @@ class Enmap extends Map {
*
* points.inc("number"); // 43
* points.inc("numberInObject", "sub.anInt"); // {sub: { anInt: 6 }}
- * @returns {Enmap} The enmap.
+ * @returns {number} The udpated value after incrementing.
*/
- inc(key, path = null) {
- this.#check(key, 'Number', path);
- if (isNil(path)) {
- let val = this.get(key);
- return this.#internalSet(key, ++val);
- } else {
- const data = this.get(key);
- let propValue = _get(data, path);
- _set(data, path, ++propValue);
- return this.#internalSet(key, data);
- }
+ inc(key) {
+ this.#keycheck(key);
+ this.#check(key, ['Number']);
+ const data = this.get(key);
+ this.set(key, data + 1);
+ return data + 1;
}
/**
@@ -547,17 +451,11 @@ class Enmap extends Map {
* points.dec("numberInObject", "sub.anInt"); // {sub: { anInt: 4 }}
* @returns {Enmap} The enmap.
*/
- dec(key, path = null) {
- this.#check(key, 'Number', path);
- if (isNil(path)) {
- let val = this.get(key);
- return this.#internalSet(key, --val);
- } else {
- const data = this.get(key);
- let propValue = _get(data, path);
- _set(data, path, --propValue);
- return this.#internalSet(key, data);
- }
+ dec(key) {
+ this.#keycheck(key);
+ const data = this.get(key);
+ this.set(key, data - 1);
+ return data - 1;
}
/**
@@ -566,7 +464,7 @@ class Enmap extends Map {
* @param {string} key Required. The key you want to make sure exists.
* @param {*} defaultValue Required. The value you want to save in the database and return as default.
* @param {string} path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* // Simply ensure the data exists (for using property methods):
* enmap.ensure("mykey", {some: "value", here: "as an example"});
@@ -578,56 +476,49 @@ class Enmap extends Map {
* console.log(settings) // enmap's value for "1234567890" if it exists, otherwise the defaultSettings value.
* @return {*} The value from the database for the key, or the default value provided for a new key.
*/
- ensure(key, defaultValue, path = null) {
- this.#readyCheck();
- this.#fetchCheck(key);
- if (this.#autoEnsure !== this.#off) {
- // eslint-disable-next-line max-len
+ ensure(key, defaultValue, path) {
+ this.#keycheck(key);
+
+ if (!isNil(this.#autoEnsure)) {
if (!isNil(defaultValue))
process.emitWarning(
`Saving "${key}" autoEnsure value was provided for this enmap but a default value has also been provided. The defaultValue will be ignored, autoEnsure value is used instead.`,
);
defaultValue = this.#autoEnsure;
}
- if (isNil(defaultValue))
- throw new Err(
- `No default value provided on ensure method for "${key}" in "${
- this.#name
- }"`,
- 'EnmapArgumentError',
- );
- const clonedValue = this.#clone(defaultValue);
+
+ const clonedDefault = cloneDeep(defaultValue);
+
if (!isNil(path)) {
if (this.#ensureProps) this.ensure(key, {});
if (this.has(key, path)) return this.get(key, path);
- this.set(key, defaultValue, path);
- return defaultValue;
+ this.set(key, clonedDefault, path);
+ return clonedDefault;
}
+
if (this.#ensureProps && isObject(this.get(key))) {
- if (!isObject(clonedValue))
+ if (!isObject(clonedDefault))
throw new Err(
`Default value for "${key}" in enmap "${
this.#name
}" must be an object when merging with an object value.`,
'EnmapArgumentError',
);
- const merged = merge(clonedValue, this.get(key));
+ const merged = merge(clonedDefault, this.get(key));
this.set(key, merged);
return merged;
}
+
if (this.has(key)) return this.get(key);
- this.set(key, clonedValue);
- return clonedValue;
+ this.set(key, clonedDefault);
+ return clonedDefault;
}
- /* BOOLEAN METHODS THAT CHECKS FOR THINGS IN ENMAP */
-
/**
* Returns whether or not the key exists in the Enmap.
* @param {string} key Required. The key of the element to add to The Enmap or array.
- * This value MUST be a string or number.
* @param {string} path Optional. The property to verify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* if(enmap.has("myKey")) {
* // key is there
@@ -636,154 +527,56 @@ class Enmap extends Map {
* if(!enmap.has("myOtherKey", "oneProp.otherProp.SubProp")) return false;
* @returns {boolean}
*/
- has(key, path = null) {
- this.#readyCheck();
- this.#fetchCheck(key);
- key = key.toString();
- if (!isNil(path)) {
- this.#check(key, 'Object');
- const data = this.get(key);
- return _has(data, path);
- }
- return super.has(key);
+ has(key) {
+ this.#keycheck(key);
+ const data = this.#db
+ .prepare(`SELECT count(*) FROM ${this.#name} WHERE key = ?`)
+ .get(key);
+ return data['count(*)'] > 0;
}
/**
* Performs Array.includes() on a certain enmap value. Works similar to
* [Array.includes()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes).
* @param {string} key Required. The key of the array to check the value of.
- * @param {string|number} val Required. The value to check whether it's in the array.
+ * @param {string|number} value Required. The value to check whether it's in the array.
* @param {string} path Optional. The property to access the array inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @return {boolean} Whether the array contains the value.
*/
- includes(key, val, path = null) {
- this.#readyCheck();
- this.#fetchCheck(key);
- this.#check(key, ['Array', 'Object']);
- const data = this.get(key);
- if (!isNil(path)) {
- const propValue = _get(data, path);
- if (isArray(propValue)) {
- return propValue.includes(val);
- }
- throw new Err(
- `The property "${path}" in key "${key}" is not an Array in the enmap "${
- this.#name
- }" (property was of type "${propValue && propValue.constructor.name}")`,
- 'EnmapTypeError',
- );
- } else if (isArray(data)) {
- return data.includes(val);
- }
- throw new Err(
- `The value of key "${key}" is not an Array in the enmap "${
- this.#name
- }" (value was of type "${data && data.constructor.name}")`,
- 'EnmapTypeError',
- );
+ includes(key, value, path) {
+ this.#keycheck(key);
+ this.#check(key, ['Array'], path);
+ const data = this.get(key, path);
+ if (!isArray(data))
+ throw new Err('Key does not point to an array', 'EnmapPathError');
+ return data.includes(value);
}
/**
* Deletes a key in the Enmap.
* @param {string} key Required. The key of the element to delete from The Enmap.
* @param {string} path Optional. The name of the property to remove from the object.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @returns {Enmap} The enmap.
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
*/
- //@ts-ignore
- delete(key, path = null) {
- this.#readyCheck();
- this.#fetchCheck(key);
- key = key.toString();
- const oldValue = this.get(key);
- if (!isNil(path)) {
- let data = this.get(key);
- //@ts-ignore
- path = toPath(path);
- //@ts-ignore
- const last = path.pop();
- const propValue = path.length ? _get(data, path) : data;
- if (isArray(propValue)) {
- propValue.splice(last, 1);
- } else {
- delete propValue[last];
- }
- if (path.length) {
- _set(data, path, propValue);
- } else {
- data = propValue;
- }
+ delete(key, path) {
+ this.#keycheck(key);
+ if (path) {
+ this.#check(key, ['Object']);
+ const data = this.get(key);
+ _set(data, path, undefined);
this.set(key, data);
} else {
- super.delete(key);
- if (typeof this.changedCB === 'function') {
- this.changedCB(key, oldValue, null);
- }
- if (this.#polling) {
- this.#db
- .prepare(
- `INSERT INTO 'internal::changes::${
- this.#name
- }' (type, key, timestamp, pid) VALUES (?, ?, ?, ?);`,
- )
- .run('delete', key.toString(), Date.now(), process.pid);
- }
this.#db.prepare(`DELETE FROM ${this.#name} WHERE key = ?`).run(key);
- return this;
- }
- return this;
- }
-
- /**
- * Deletes everything from the enmap. If persistent, clears the database of all its data for this table.
- */
- deleteAll() {
- this.#readyCheck();
- this.#db.prepare(`DELETE FROM ${this.#name};`).run();
- if (this.#polling) {
- this.#db
- .prepare(
- `INSERT INTO 'internal::changes::${
- this.#name
- }' (type, timestamp, pid) VALUES (?, ?, ?);`,
- )
- .run('clear', Date.now(), process.pid);
}
- super.clear();
}
/**
- * Deletes everything from the enmap. If persistent, clears the database of all its data for this table.
+ * Deletes everything from the enmap.
* @returns {void}
*/
clear() {
- return this.deleteAll();
- }
-
- /**
- * 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.
- * @returns {null}
- */
- destroy() {
- this.deleteAll();
-
- this.#isDestroyed = true;
-
- const transaction = this.#db.transaction((run) => {
- for (const stmt of run) {
- this.#db.prepare(stmt).run();
- }
- });
-
- transaction([
- `DROP TABLE IF EXISTS ${this.#name};`,
- `DROP TABLE IF EXISTS 'internal::changes::${this.#name}';`,
- `DELETE FROM 'internal::autonum' WHERE enmap = '${this.#name}';`,
- ]);
- return null;
+ this.#db.prepare(`DELETE FROM ${this.#name}`).run();
}
/**
@@ -791,11 +584,10 @@ class Enmap extends Map {
* 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.
* @param {string} key Required. The key of the element to remove from in Enmap.
- * This value MUST be a string or number.
* @param {*|Function} val 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.
* @param {string} path Optional. The name of the array property to remove from.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3".
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3".
* If not presents, removes directly from the value.
* @example
* // Assuming
@@ -804,31 +596,17 @@ class Enmap extends Map {
*
* enmap.remove('array', 1); // value is now [2, 3]
* enmap.remove('objectarray', (value) => value.e === 5); // value is now [{ a: 1, b: 2, c: 3 }]
- * @returns {Enmap} The enmap.
*/
- remove(key, val, path = null) {
- this.#readyCheck();
- this.#fetchCheck(key);
+ remove(key, val, path) {
+ this.#keycheck(key);
this.#check(key, ['Array', 'Object']);
const data = this.get(key, path);
- if (!Array.isArray(data)) {
- if (path)
- throw new Err(
- `The property "${path}" in key "${key}" is an Array in the enmap "${this.#name}" (key was of type "${data?.constructor.name ?? typeof data}")`,
- 'EnmapTypeError',
- );
- else
- throw new Err(
- `The value for key "${key}" is not an Array in the enmap "${this.#name}" (value was of type "${data?.constructor.name ?? typeof data}")`,
- 'EnmapTypeError',
- );
- }
const criteria = isFunction(val) ? val : (value) => val === value;
const index = data.findIndex(criteria);
if (index > -1) {
data.splice(index, 1);
}
- return this.set(key, data, path);
+ this.set(key, data, path);
}
/**
@@ -837,54 +615,48 @@ class Enmap extends Map {
* @returns {string} The enmap data in a stringified JSON format.
*/
export() {
- this.#readyCheck();
- this.fetchEverything();
- return serialize(
- {
- name: this.#name,
- version: pkgdata.version,
- exportDate: Date.now(),
- keys: this.map((value, key) => ({ key, value })),
- },
- {
- space: 2,
- },
- );
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ const entries = [];
+ for (const row of stmt.iterate()) {
+ entries.push(row);
+ }
+ return stringify({
+ name: this.#name,
+ exportDate: Date.now(),
+ version: pkgdata.version,
+ keys: entries,
+ });
}
/**
- * Import an existing json export from enmap from a string. This data must have been exported from enmap,
+ * Import an existing json export from enmap. 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.
- * @param {string} data The data to import to Enmap. Must contain all the required fields provided by export()
+ * (This means Enmap 5 data is compatible in Enmap 6).
+ * @param {string} data The data to import to Enmap. Must contain all the required fields provided by an enmap export().
* @param {boolean} overwrite Defaults to `true`. Whether to overwrite existing key/value data with incoming imported data
* @param {boolean} clear Defaults to `false`. Whether to clear the enmap of all data before importing
* (**__WARNING__**: Any existing data will be lost! This cannot be undone.)
- * @returns {Enmap} The enmap with the new data.
*/
import(data, overwrite = true, clear = false) {
- this.#readyCheck();
- if (clear) this.deleteAll();
- if (isNil(data))
- throw new Err(
- `No data provided for import() in "${this.#name}"`,
- 'EnmapImportError',
- );
+ let parsedData;
try {
- const parsed = eval(`(${data})`);
- for (const thisEntry of parsed.keys) {
- const { key, value } = thisEntry;
- if (!overwrite && this.has(key)) continue;
- this.#internalSet(key, this.#deserializer(value, key));
- }
- } catch (err) {
+ parsedData = JSON.parse(data);
+ } catch (e) {
+ throw new Err('Data provided is not valid JSON', 'EnmapDataError');
+ }
+
+ if (isNil(parsedData))
throw new Err(
- `Data provided for import() in "${
- this.#name
- }" is invalid JSON. Stacktrace:\n${err}`,
+ `No data provided for import() in "${this.#name}"`,
'EnmapImportError',
);
+
+ if (clear) this.clear();
+ for (const entry of parsedData.keys) {
+ const { key, value } = entry;
+ if (!overwrite && this.has(key)) continue;
+ this.#set(key, this.#parse(value));
}
- return this;
}
/**
@@ -902,406 +674,141 @@ class Enmap extends Map {
*
* @returns {Object} An array of initialized Enmaps.
*/
- static multi(names, options = {}) {
+ static multi(names, options) {
if (!names.length || names.length < 1) {
throw new Err(
'"names" argument must be an array of string names.',
'EnmapTypeError',
);
}
-
- const returnvalue = {};
+ const enmaps = {};
for (const name of names) {
- const enmap = new Enmap({ name, ...options });
- returnvalue[name] = enmap;
+ enmaps[name] = new Enmap({ ...options, name });
}
- return returnvalue;
+ return enmaps;
}
- /* INTERNAL (Private) METHODS */
-
- /*
- * Internal Method. Initializes the enmap depending on given values.
- * @param {Map} database In order to set data to the Enmap, one must be provided.
+ /**
+ * Obtains random value(s) from this Enmap. This relies on {@link Enmap#array}.
+ * @param {number} [count] Number of values to obtain randomly
+ * @returns {*|Array<*>} The single value if `count` is undefined,
+ * or an array of values of `count` length
*/
- #init(database) {
- this.#db = database;
- if (!this.#db) {
- throw new Err('Database Could Not Be Opened', 'EnmapDBConnectionError');
- }
- const table = this.#db
- .prepare(
- "SELECT count(*) FROM sqlite_master WHERE type='table' AND name = ?;",
- )
- .get(this.#name);
- if (!table['count(*)']) {
- this.#db
- .prepare(
- `CREATE TABLE ${this.#name} (key text PRIMARY KEY, value text)`,
- )
- .run();
- this.#db.pragma('synchronous = 1');
- if (this.#wal) this.#db.pragma('journal_mode = wal');
- }
- this.#db
- .prepare(
- `CREATE TABLE IF NOT EXISTS 'internal::changes::${
- this.#name
- }' (type TEXT, key TEXT, value TEXT, timestamp INTEGER, pid INTEGER);`,
- )
- .run();
- this.#db
- .prepare(
- `CREATE TABLE IF NOT EXISTS 'internal::autonum' (enmap TEXT PRIMARY KEY, lastnum INTEGER)`,
- )
- .run();
- if (this.#fetchAll) {
- this.fetchEverything();
- }
- // TEMPORARY MIGRATE CODE FOR AUTONUM
- // REMOVE FOR V6
- if (this.has('internal::autonum')) {
- this.#db
- .prepare(
- "INSERT OR REPLACE INTO 'internal::autonum' (enmap, lastnum) VALUES (?, ?)",
- )
- .run(this.#name, this.get('internal::autonum'));
- this.delete('internal::autonum');
- } else {
- const row = this.#db
- .prepare("SELECT lastnum FROM 'internal::autonum' WHERE enmap = ?")
- .get(this.#name);
- if (!row) {
- this.#db
- .prepare(
- "INSERT INTO 'internal::autonum' (enmap, lastnum) VALUES (?, ?)",
- )
- .run(this.#name, 0);
- }
+ random(count = 1) {
+ const stmt = this.#db
+ .prepare(`SELECT key, value FROM ${this.#name} ORDER BY RANDOM() LIMIT ?`)
+ .bind(count);
+ const results = [];
+ for (const row of stmt.iterate()) {
+ results.push([row.key, this.#parse(row.value)]);
}
+ return results;
+ }
- if (this.#polling) {
- this.#lastSync = new Date();
- setInterval(() => {
- const changes = this.#db
- .prepare(
- `SELECT type, key, value FROM 'internal::changes::${
- this.#name
- }' WHERE timestamp >= ? AND pid <> ? ORDER BY timestamp ASC;`,
- )
- .all(this.#lastSync.getTime(), process.pid);
- for (const row of changes) {
- switch (row.type) {
- case 'insert':
- super.set(row.key, this.#parseData(row.value, row.key));
- break;
- case 'delete':
- super.delete(row.key);
- break;
- case 'clear':
- super.clear();
- break;
- }
- }
- this.#lastSync = new Date();
- this.#db
- .prepare(
- `DELETE FROM 'internal::changes::${
- this.#name
- }' WHERE ROWID IN (SELECT ROWID FROM 'internal::changes::${
- this.#name
- }' ORDER BY ROWID DESC LIMIT -1 OFFSET 100);`,
- )
- .run();
- }, this.#pollingInterval);
+ /**
+ * Obtains random key(s) from this Enmap. This relies on {@link Enmap#keyArray}
+ * @param {number} [count] Number of keys to obtain randomly
+ * @returns {*|Array<*>} The single key if `count` is undefined,
+ * or an array of keys of `count` length
+ */
+ randomKey(count = 1) {
+ const stmt = this.#db
+ .prepare(`SELECT key FROM ${this.#name} ORDER BY RANDOM() LIMIT ?`)
+ .bind(count);
+ const results = [];
+ for (const row of stmt.iterate()) {
+ results.push(row.key);
}
- }
-
- /*
- * INTERNAL method to verify the type of a key or property
- * Will THROW AN ERROR on wrong type, to simplify code.
- * @param {string} key Required. The key of the element to check
- * @param {string} type Required. The javascript constructor to check
- * @param {string} path Optional. The dotProp path to the property in the object enmap.
- */
- #check(key, type, path = null) {
- key = key.toString();
- if (!this.has(key))
- throw new Err(
- `The key "${key}" does not exist in the enmap "${this.#name}"`,
- 'EnmapPathError',
- );
- if (!type) return;
- if (!isArray(type)) type = [type];
- if (!isNil(path)) {
- this.#check(key, 'Object');
- const data = this.get(key);
- if (isNil(_get(data, path))) {
- throw new Err(
- `The property "${path}" in key "${key}" does not exist. Please set() it or ensure() it."`,
- 'EnmapPathError',
- );
- }
- if (!type.includes(_get(data, path).constructor.name)) {
- throw new Err(
- `The property "${path}" in key "${key}" is not of type "${type.join(
- '" or "',
- )}" in the enmap "${this.#name}"
-(key was of type "${_get(data, path).constructor.name}")`,
- 'EnmapTypeError',
- );
- }
- } else if (!type.includes(this.get(key).constructor.name)) {
- throw new Err(
- `The value for key "${key}" is not of type "${type.join(
- '" or "',
- )}" in the enmap "${this.#name}" (value was of type "${
- this.get(key).constructor.name
- }")`,
- 'EnmapTypeError',
- );
- }
- }
-
- /*
- * INTERNAL method to execute a mathematical operation. Cuz... javascript.
- * And I didn't want to import mathjs!
- * @param {number} base the lefthand operand.
- * @param {string} op the operation.
- * @param {number} opand the righthand operand.
- * @return {number} the result.
- */
- #mathop(base, op, opand) {
- if (base == undefined || op == undefined || opand == undefined)
- throw new Err(
- 'Math Operation requires base and operation',
- 'EnmapTypeError',
- );
- switch (op) {
- case 'add':
- case 'addition':
- case '+':
- return base + opand;
- case 'sub':
- case 'subtract':
- case '-':
- return base - opand;
- case 'mult':
- case 'multiply':
- case '*':
- return base * opand;
- case 'div':
- case 'divide':
- case '/':
- return base / opand;
- case 'exp':
- case 'exponent':
- case '^':
- return Math.pow(base, opand);
- case 'mod':
- case 'modulo':
- case '%':
- return base % opand;
- case 'rand':
- case 'random':
- return Math.floor(Math.random() * Math.floor(opand));
- }
- return null;
+ return results;
}
/**
- * Internal method used to validate persistent enmap names (valid Windows filenames)
- */
- #validateName() {
- this.#name = this.#name.replace(/[^a-z0-9]/gi, '_').toLowerCase();
- }
-
- /*
- * Internal Method. Verifies if a key needs to be fetched from the database.
- * If persistent enmap and autoFetch is on, retrieves the key.
- * @param {string} key The key to check or fetch.
- */
- #fetchCheck(key, force = false) {
- key = key.toString();
- if (!['String', 'Number'].includes(key.constructor.name)) return;
- if (force) {
- this.fetch(key);
- return;
- }
- if (super.has(key)) return;
- if (!this.#autoFetch) return;
- this.fetch(key);
- }
-
- /*
- * Internal Method. Parses JSON data.
- * Reserved for future use (logical checking)
- * @param {*} data The data to check/parse
- * @returns {*} An object or the original data.
- */
- #parseData(data, key) {
- return this.#deserializer(eval(`(${data})`), key);
- }
-
- /*
- * Internal Method. Clones a value or object with the enmap's set clone level.
- * @param {*} data The data to clone.
- * @return {*} The cloned value.
- */
- #clone(data) {
- if (this.#cloneLevel === 'none') return data;
- if (this.#cloneLevel === 'shallow') return clone(data);
- if (this.#cloneLevel === 'deep') return cloneDeep(data);
- throw new Err(
- "Invalid cloneLevel. What did you *do*, this shouldn't happen!",
- 'EnmapOptionsError',
- );
- }
-
- /*
- * Internal Method. Verifies that the database is ready, assuming persistence is used.
- */
- #readyCheck() {
- if (this.#isDestroyed)
- throw new Err(
- 'This enmap has been destroyed and can no longer be used without being re-initialized.',
- 'EnmapDestroyedError',
- );
- }
-
- /*
- * Internal Method. Sets data without looking at cache, fetching, or anything else. Used when fetch/ready checks are already made.
+ * Similar to
+ * [Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).
+ * Supports either a predicate function or a value to compare.
+ * Returns true only if the predicate function returns true for all elements in the array (or the value is strictly equal in all elements).
+ * @param {Function | string} valueOrFunction Function used to test (should return a boolean), or a value to compare.
+ * @param {string} [path] Required if the value is an object. The path to the property to compare with.
+ * @returns {boolean}
*/
- #internalSet(key, value, updateCache = true) {
- let serialized;
- try {
- serialized = serialize(this.#serializer(value, key));
- } catch (e) {
- serialized = serialize(this.#serializer(onChange.target(value), key));
- }
- this.#db
- .prepare(
- `INSERT OR REPLACE INTO ${this.#name} (key, value) VALUES (?, ?);`,
- )
- .run(key, serialized);
- if (this.#polling) {
- this.#db
- .prepare(
- `INSERT INTO 'internal::changes::${
- this.#name
- }' (type, key, value, timestamp, pid) VALUES (?, ?, ?, ?, ?);`,
- )
- .run('insert', key, serialized, Date.now(), process.pid);
+ every(valueOrFunction, path) {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const data = isNil(path) ? parsed : _get(parsed, path);
+ if (isFunction(valueOrFunction)) {
+ if (!valueOrFunction(data, row.key)) {
+ return false;
+ }
+ } else {
+ if (valueOrFunction !== data) {
+ return false;
+ }
+ }
}
- if (updateCache) super.set(key, value);
- return this;
+ return true;
}
- /*
- BELOW IS DISCORD.JS COLLECTION CODE
- Per notes in the LICENSE file, this project contains code from Amish Shah's Discord.js
- library. The code is from the Collections object, in discord.js version 11.
-
- All below code is sourced from Collections.
- https://github.com/discordjs/collection
- */
-
/**
- * 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.
+ * Similar to
+ * [Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
+ * Supports either a predicate function or a value to compare.
+ * Returns true if the predicate function returns true for at least one element in the array (or the value is equal in at least one element).
+ * @param {Function | string} valueOrFunction Function used to test (should return a boolean), or a value to compare.
+ * @param {string} [path] Required if the value is an object. The path to the property to compare with.
* @returns {Array}
*/
- array() {
- return Array.from(this.values());
- }
-
- /**
- * 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.
- * @returns {Array}
- */
- keyArray() {
- return Array.from(this.keys());
- }
-
- /**
- * Obtains random value(s) from this Enmap. This relies on {@link Enmap#array}.
- * @param {number} [count] Number of values to obtain randomly
- * @returns {*|Array<*>} The single value if `count` is undefined,
- * or an array of values of `count` length
- */
- random(count) {
- let arr = this.array();
- if (count === undefined) return arr[Math.floor(Math.random() * arr.length)];
- if (typeof count !== 'number')
- throw new TypeError('The count must be a number.');
- if (!Number.isInteger(count) || count < 1)
- throw new RangeError('The count must be an integer greater than 0.');
- if (arr.length === 0) return [];
- const rand = new Array(count);
- arr = arr.slice();
- for (let i = 0; i < count; i++)
- rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
- return rand;
- }
-
- /**
- * Obtains random key(s) from this Enmap. This relies on {@link Enmap#keyArray}
- * @param {number} [count] Number of keys to obtain randomly
- * @returns {*|Array<*>} The single key if `count` is undefined,
- * or an array of keys of `count` length
- */
- randomKey(count) {
- let arr = this.keyArray();
- if (count === undefined) return arr[Math.floor(Math.random() * arr.length)];
- if (typeof count !== 'number')
- throw new TypeError('The count must be a number.');
- if (!Number.isInteger(count) || count < 1)
- throw new RangeError('The count must be an integer greater than 0.');
- if (arr.length === 0) return [];
- const rand = new Array(count);
- arr = arr.slice();
- // eslint-disable-next-line
- for (let i = 0; i < count; i++)
- rand[i] = arr.splice(Math.floor(Math.random() * arr.length), 1)[0];
- return rand;
+ some(valueOrFunction, path) {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const data = isNil(path) ? parsed : _get(parsed, path);
+ if (isFunction(valueOrFunction)) {
+ if (valueOrFunction(data, row.key)) {
+ return true;
+ }
+ } else {
+ if (valueOrFunction === data) {
+ return true;
+ }
+ }
+ }
+ return false;
}
/**
- * Searches for all items where their specified property's value is identical to the given value
- * (`item[prop] === value`).
- * @param {string} prop The property to test against
- * @param {*} value The expected value
+ * Similar to
+ * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
+ * Returns an array of the results of applying the callback to all elements.
+ * @param {Function | string} pathOrFn A function that produces an element of the new Array, or a path to the property to map.
* @returns {Array}
- * @example
- * enmap.findAll('username', 'Bob');
*/
- findAll(prop, value) {
- if (typeof prop !== 'string') throw new TypeError('Key must be a string.');
- if (isNil(value)) throw new Error('Value must be specified.');
- const results = [];
- for (const item of this.values()) {
- if (
- item[prop] === value ||
- (isObject(item) && _get(item, prop) === value)
- )
- results.push(item);
+ map(pathOrFn) {
+ this.#db.aggregate('map', {
+ start: [],
+ step: (accumulator, value, key) => {
+ const parsed = this.#parse(value);
+ if (isFunction(pathOrFn)) {
+ accumulator.push(pathOrFn(parsed, key));
+ } else {
+ accumulator.push(_get(parsed, pathOrFn));
+ }
+ return accumulator;
+ },
+ result: (accumulator) => JSON.stringify(accumulator),
+ });
+ const results = this.#db
+ .prepare(`SELECT map(value, key) FROM ${this.#name}`)
+ .pluck()
+ .get();
+ return JSON.parse(results);
}
- return results;
- }
/**
* 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
+ * (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is similar to
* [Array.find()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find).
- * 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.
- * @param {string|Function} propOrFn The property to test against, or the function to test with
+ * @param {string|Function} pathOrFn The path to the value to test against, or the function to test with
* @param {*} [value] The expected value - only applicable and required if using a property for the first argument
* @returns {*}
* @example
@@ -1309,457 +816,325 @@ class Enmap extends Map {
* @example
* enmap.find(val => val.username === 'Bob');
*/
- find(propOrFn, value) {
- this.#readyCheck();
- if (isNil(propOrFn) || (!isFunction(propOrFn) && isNil(value))) {
- throw new Err(
- 'find requires either a prop and value, or a function. One of the provided arguments was null or undefined',
- 'EnmapArgumentError',
- );
- }
- const func = isFunction(propOrFn)
- ? propOrFn
- : //@ts-ignore
- (v) => value === _get(v, propOrFn);
- for (const [key, val] of this) {
- if (func(val, key, this)) return val;
+ find(pathOrFn, value) {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const func = isFunction(pathOrFn)
+ ? pathOrFn
+ : (v) => value === _get(v, pathOrFn);
+ if (func(parsed, row.key)) {
+ return parsed;
+ }
}
return null;
}
/**
* 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
+ * (`item[prop] === value`), or the given function returns a truthy value. In the latter case, this is similar to
* [Array.findIndex()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex).
- * @param {string|Function} propOrFn The property to test against, or the function to test with
+ * @param {string|Function} pathOrFn The path to the value to test against, or the function to test with
* @param {*} [value] The expected value - only applicable and required if using a property for the first argument
* @returns {string|number}
* @example
- * enmap.findKey('username', 'Bob');
+ * enmap.findIndex('username', 'Bob');
* @example
- * enmap.findKey(val => val.username === 'Bob');
+ * enmap.findIndex(val => val.username === 'Bob');
*/
- findKey(propOrFn, value) {
- this.#readyCheck();
- if (typeof propOrFn === 'string') {
- if (isNil(value)) throw new Error('Value must be specified.');
- for (const [key, val] of this) {
- if (
- val[propOrFn] === value ||
- (isObject(val) && _get(val, propOrFn) === value)
- )
- return key;
+ findIndex(pathOrFn, value) {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const func = isFunction(pathOrFn)
+ ? pathOrFn
+ : (v) => value === _get(v, pathOrFn);
+ if (func(parsed, row.key)) {
+ return row.key;
}
- return null;
- } else if (typeof propOrFn === 'function') {
- for (const [key, val] of this) {
- if (propOrFn(val, key, this)) return key;
- }
- return null;
- }
- throw new Error('First argument must be a property string or a function.');
- }
-
- /**
- * Removes entries that satisfy the provided filter function.
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {Object} [thisArg] Value to use as `this` when executing function
- * @returns {number} The number of removed entries
- */
- sweep(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- const previousSize = this.size;
- for (const [key, val] of this) {
- if (fn(val, key, this)) this.delete(key);
}
- return previousSize - this.size;
+ return null;
}
/**
- * Identical to
- * [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter),
- * but returns a Enmap instead of an Array.
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {Object} [thisArg] Value to use as `this` when executing function
- * @returns {Enmap}
+ * Similar to
+ * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
+ * @param {Function} predicate Function used to reduce, taking three arguments; `accumulator`, `currentValue`, `currentKey`.
+ * @param {*} [initialValue] Starting value for the accumulator
+ * @returns {*}
*/
- filter(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- const results = new Enmap();
- for (const [key, val] of this) {
- if (fn(val, key, this)) results.set(key, val);
- }
- return results;
+ reduce(predicate, initialValue) {
+ this.#db.aggregate('reduce', {
+ start: initialValue,
+ step: (accumulator, currentValue, key) =>
+ predicate(accumulator, this.#parse(currentValue), key),
+ });
+ return this.#db
+ .prepare(`SELECT reduce(value, key) FROM ${this.#name}`)
+ .pluck()
+ .get();
}
/**
- * Identical to
+ * Similar to
* [Array.filter()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {Object} [thisArg] Value to use as `this` when executing function
- * @returns {Array}
- */
- filterArray(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- const results = [];
- for (const [key, val] of this) {
- if (fn(val, key, this)) results.push(val);
- }
- return results;
- }
-
- /**
- * Identical to
- * [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
- * @param {Function} fn Function that produces an element of the new array, taking three arguments
- * @param {*} [thisArg] Value to use as `this` when executing function
- * @returns {Array}
+ * Returns an array of values where the given function returns true for that value.
+ * Alternatively you can provide a value and path to filter by using exact value matching.
+ * @param {Function} pathOrFn The path to the value to test against, or the function to test with.
+ * If using a function, this function should return a boolean.
+ * @param {string} [value] Value to use as `this` when executing function
+ * @returns {Enmap}
*/
- map(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- const arr = new Array(this.size);
- let i = 0;
- for (const [key, val] of this) arr[i++] = fn(val, key, this);
- return arr;
+ filter(pathOrFn, value) {
+ this.#db.aggregate('filter', {
+ start: [],
+ step: (accumulator, currentValue, key) => {
+ const parsed = this.#parse(currentValue);
+ if (isFunction(pathOrFn)) {
+ if (pathOrFn(parsed, key)) {
+ accumulator.push(parsed);
+ }
+ } else {
+ if (!value)
+ throw new Err(
+ 'Value is required for non-function predicate',
+ 'EnmapValueError',
+ );
+ const pathValue = _get(parsed, pathOrFn);
+ if (value === pathValue) {
+ accumulator.push(parsed);
+ }
+ }
+ return accumulator;
+ },
+ result: (accumulator) => JSON.stringify(accumulator),
+ });
+ const results = this.#db
+ .prepare(`SELECT filter(value, key) FROM ${this.#name}`)
+ .pluck()
+ .get();
+ return JSON.parse(results);
}
/**
- * Identical to
- * [Array.some()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some).
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {Object} [thisArg] Value to use as `this` when executing function
- * @returns {boolean}
+ * Deletes entries that satisfy the provided filter function or value matching.
+ * @param {Function|string} pathOrFn The path to the value to test against, or the function to test with.
+ * @param {*} [value] The expected value - only applicable and required if using a property for the first argument.
+ * @returns {number} The number of removed entries.
*/
- some(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- for (const [key, val] of this) {
- if (fn(val, key, this)) return true;
+ sweep(pathOrFn, value) {
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ const deleteStmt = this.#db.prepare(`DELETE FROM ${this.#name} WHERE key = ?`);
+ const deleteKeys = [];
+ const deleteMany = this.#db.transaction((cats) => {
+ for (const cat of cats) deleteStmt.run(cat);
+ });
+ let count = 0;
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const data = isNil(value) ? parsed : _get(parsed, pathOrFn);
+ if (isFunction(pathOrFn)) {
+ if (pathOrFn(data, row.key)) {
+ count++;
+ deleteKeys.push(row.key);
+ }
+ } else {
+ if (value === data) {
+ count++;
+ deleteKeys.push(row.key);
+ }
+ }
}
- return false;
+ deleteMany(deleteKeys);
+ return count;
}
/**
- * Identical to
- * [Array.every()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every).
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {Object} [thisArg] Value to use as `this` when executing function
- * @returns {boolean}
+ * 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
+ * enmap.changed((keyName, oldValue, newValue) => {
+ * console.log(`Value of ${keyName} has changed from: \n${oldValue}\nto\n${newValue}`);
+ * });
+ * @param {Function} cb A callback function that will be called whenever data changes in the enmap.
*/
- every(fn, thisArg) {
- this.#readyCheck();
- if (thisArg) fn = fn.bind(thisArg);
- for (const [key, val] of this) {
- if (!fn(val, key, this)) return false;
- }
- return true;
+ changed(cb) {
+ this.#changedCB = cb;
}
/**
- * Identical to
- * [Array.reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce).
- * @param {Function} fn Function used to reduce, taking four arguments; `accumulator`, `currentValue`, `currentKey`,
- * and `enmap`
- * @param {*} [initialValue] Starting value for the accumulator
- * @returns {*}
+ * Separates the Enmap into multiple arrays given a function that separates them.
+ * @param {*} pathOrFn the path to the value to test against, or the function to test with.
+ * @param {*} value the value to use as a condition for partitioning.
+ * @returns {Array>} An array of arrays with the partitioned data.
*/
- reduce(fn, initialValue) {
- this.#readyCheck();
- let accumulator;
- if (typeof initialValue !== 'undefined') {
- accumulator = initialValue;
- for (const [key, val] of this)
- accumulator = fn(accumulator, val, key, this);
- } else {
- let first = true;
- for (const [key, val] of this) {
- if (first) {
- accumulator = val;
- first = false;
- continue;
+ partition(pathOrFn, value) {
+ const results = [[], []];
+ const stmt = this.#db.prepare(`SELECT key, value FROM ${this.#name}`);
+ for (const row of stmt.iterate()) {
+ const parsed = this.#parse(row.value);
+ const data = isNil(value) ? parsed : _get(parsed, pathOrFn);
+ if (isFunction(pathOrFn)) {
+ if (pathOrFn(data, row.key)) {
+ results[0].push(parsed);
+ } else {
+ results[1].push(parsed);
+ }
+ } else {
+ if (value === data) {
+ results[0].push(parsed);
+ } else {
+ results[1].push(parsed);
}
- accumulator = fn(accumulator, val, key, this);
}
}
- return accumulator;
+ return results;
}
- /**
- * Creates an identical shallow copy of this Enmap.
- * @returns {Enmap}
- * @example const newColl = someColl.clone();
- */
- clone() {
- this.#readyCheck();
- return new Enmap(this);
- }
+ // INTERNAL METHODS
- /**
- * Combines this Enmap with others into a new Enmap. None of the source Enmaps are modified.
- * @param {...Enmap} enmaps Enmaps to merge
- * @returns {Enmap}
- * @example const newColl = someColl.concat(someOtherColl, anotherColl, ohBoyAColl);
+ /*
+ * Internal method used to insert or update a key in the database without circular calls to ensure() or others.
+ * @param {string} key Key to update in database
+ * @param {*} value value to save in database
+ * Path is not supported in this method as it writes the whole key.
*/
- concat(...enmaps) {
- this.#readyCheck();
- const newColl = this.clone();
- for (const coll of enmaps) {
- for (const [key, val] of coll) newColl.set(key, val);
+ #set(key, value) {
+ let serialized;
+ try {
+ serialized = stringify(this.#serializer(value, key));
+ } catch (e) {
+ serialized = stringify(this.#serializer(onChange.target(value), key));
}
- return newColl;
+ this.#db
+ .prepare(
+ `INSERT OR REPLACE INTO ${this.#name} (key, value) VALUES (?, ?)`,
+ )
+ .run(key, serialized);
}
- /* DEPRECATED METHODS */
- /* TO BE REMOVED IN VERSION 6 */
-
- /**
- * Partitions the enmap into two enmaps where the first enmap
- * contains the items that passed and the second contains the items that failed.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6!
- * @param {Function} fn Function used to test (should return a boolean)
- * @param {*} [thisArg] Value to use as `this` when executing function
- * @returns {Enmap[]}
- * @example const [big, small] = enmap.partition(guild => guild.memberCount > 250);
- * @deprecated Will be removed in Enmap 6!
+ /*
+ * Internal Method. Parses JSON data.
+ * Reserved for future use (logical checking)
+ * @param {*} value The data to check/parse
+ * @returns {*} An object or the original data.
*/
- partition(fn, thisArg) {
- process.emitWarning(
- 'ENMAP DEPRECATION partition() will be removed in the next major Enmap release (v6)!',
- );
- this.#readyCheck();
- if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
- const results = [new Enmap(), new Enmap()];
- for (const [key, val] of this) {
- if (fn(val, key, this)) {
- results[0].set(key, val);
- } else {
- results[1].set(key, val);
+ #parse(value) {
+ let parsed;
+ try {
+ parsed = parse(value);
+ try {
+ parsed = this.#deserializer(parsed);
+ } catch (e) {
+ throw new Err('Error while deserializing data: ', e.message, 'EnmapParseError');
}
+ } catch (e) {
+ throw new Err('Error while deserializing data: ', e.message, 'EnmapParseError');
}
- return results;
- }
-
- /**
- * Checks if this Enmap shares identical key-value pairings with another.
- * This is different to checking for equality using equal-signs, because
- * the Enmaps may be different objects, but contain the same data.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6!
- * @param {Enmap} enmap Enmap to compare with
- * @returns {boolean} Whether the Enmaps have identical contents
- * @deprecated Will be removed in Enmap 6!
- */
- equals(enmap) {
- process.emitWarning(
- 'ENMAP DEPRECATION equals() will be removed in the next major Enmap release (v6)!',
- );
- this.#readyCheck();
- if (!enmap) return false;
- if (this === enmap) return true;
- if (this.size !== enmap.size) return false;
- return !this.find((value, key) => {
- const testVal = enmap.get(key);
- return testVal !== value || (testVal === undefined && !enmap.has(key));
- });
- }
-
- /**
- * Modify the property of a value inside the enmap, if the value is an object or array.
- * This is a shortcut to loading the key, changing the value, and setting it back.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use set() instead!
- * @param {string} key Required. The key of the element to add to The Enmap or array.
- * This value MUST be a string or number.
- * @param {string} path Required. The property to modify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @param {*} val Required. The value to apply to the specified property.
- * @returns {Enmap} The enmap.
- * @deprecated Will be removed in Enmap 6!
- */
- setProp(key, path, val) {
- process.emitWarning(
- 'ENMAP DEPRECATION setProp() will be removed in the next major Enmap release (v6)! Please use set(key, value, path) instead.',
- );
- this.#readyCheck();
- if (isNil(path))
- throw new Err(
- `No path provided to set a property in "${key}" of enmap "${
- this.#name
- }"`,
- 'EnmapPathError',
- );
- return this.set(key, val, path);
+ return parsed;
}
- /**
- * Push to an array element inside an Object or Array element in Enmap.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use push() instead!
- * @param {string} key Required. The key of the element.
- * This value MUST be a string or number.
- * @param {string} path Required. The name of the array property to push to.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @param {*} val Required. The value push to the array property.
- * @param {boolean} allowDupes Allow duplicate values in the array (default: false).
- * @returns {Enmap} The enmap.
- * @deprecated Will be removed in Enmap 6!
- */
- pushIn(key, path, val, allowDupes = false) {
- process.emitWarning(
- 'ENMAP DEPRECATION pushIn() will be removed in the next major Enmap release (v6)! Please use push(key, value, path) instead.',
- );
- this.#readyCheck();
- this.#fetchCheck(key);
- if (isNil(path))
- throw new Err(
- `No path provided to push a value in "${key}" of enmap "${this.#name}"`,
- 'EnmapPathError',
- );
- return this.push(key, val, path, allowDupes);
- }
+ /* INTERNAL METHOD */
- /**
- * Returns the specific property within a stored value. If the key does not exist or the value is not an object, throws an error.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use get() instead!
- * @param {string} key Required. The key of the element to get from The Enmap.
- * @param {string} path Required. The property to retrieve from the object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @return {*} The value of the property obtained.
- * @deprecated Will be removed in Enmap 6!
- */
- getProp(key, path) {
- process.emitWarning(
- 'ENMAP DEPRECATION getProp() will be removed in the next major Enmap release (v6)! Please use get(key, path) instead.',
- );
- this.#readyCheck();
- this.#fetchCheck(key);
- if (isNil(path))
- throw new Err(
- `No path provided to get a property from "${key}" of enmap "${
- this.#name
- }"`,
- 'EnmapPathError',
+ #keycheck(key, type = 'key') {
+ if (!NAME_REGEX.test(key)) {
+ throw new Error(
+ `Invalid ${type} for enmap - only alphanumeric characters, underscores and hyphens are allowed.`,
);
- return this.get(key, path);
+ }
}
- /**
- * Delete a property from an object or array value in Enmap.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use delete() instead!
- * @param {string} key Required. The key of the element to delete the property from in Enmap.
- * @param {string} path Required. The name of the property to remove from the object.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @deprecated Will be removed in Enmap 6! Use delete() instead!
+ /*
+ * INTERNAL method to verify the type of a key or property
+ * Will THROW AN ERROR on wrong type, to simplify code.
+ * @param {string} key Required. The key of the element to check
+ * @param {string} type Required. The javascript constructor to check
+ * @param {string} path Optional. The dotProp path to the property in the object enmap.
*/
- deleteProp(key, path) {
- process.emitWarning(
- 'ENMAP DEPRECATION deleteProp() will be removed in the next major Enmap release (v6)! Please use delete(key, path) instead.',
- );
- this.#readyCheck();
- this.#fetchCheck(key);
- if (isNil(path))
+ #check(key, type, path) {
+ key = key.toString();
+ if (!this.has(key))
throw new Err(
- `No path provided to delete a property in "${key}" of enmap "${
- this.#name
- }"`,
+ `The key "${key}" does not exist in the enmap "${this.#name}"`,
'EnmapPathError',
);
- this.delete(key, path);
- }
-
- /**
- * Remove a value from an Array or Object property inside an Array or Object element in Enmap.
- * Confusing? Sure is.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use remove() instead!
- * @param {string} key Required. The key of the element.
- * This value MUST be a string or number.
- * @param {string} path Required. The name of the array property to remove from.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @param {*} val Required. The value to remove from the array property.
- * @returns {Enmap} The enmap.
- * @deprecated Will be removed in Enmap 6! Use remove() instead!
- */
- removeFrom(key, path, val) {
- process.emitWarning(
- 'ENMAP DEPRECATION removeFrom() will be removed in the next major Enmap release (v6)! Please use remove(key, value, path) instead.',
- );
- this.#readyCheck();
- this.#fetchCheck(key);
- if (isNil(path))
+ if (!type) return;
+ if (!isArray(type)) type = [type];
+ if (!isNil(path)) {
+ this.#check(key, 'Object');
+ const data = this.get(key);
+ if (isNil(_get(data, path))) {
+ throw new Err(
+ `The property "${path}" in key "${key}" does not exist. Please set() it or ensure() it."`,
+ 'EnmapPathError',
+ );
+ }
+ if (!type.includes(_get(data, path).constructor.name)) {
+ throw new Err(
+ `The property "${path}" in key "${key}" is not of type "${type.join(
+ '" or "',
+ )}" in the enmap "${this.#name}"
+ (key was of type "${_get(data, path).constructor.name}")`,
+ 'EnmapTypeError',
+ );
+ }
+ } else if (!type.includes(this.get(key).constructor.name)) {
throw new Err(
- `No path provided to remove an array element in "${key}" of enmap "${
- this.#name
- }"`,
- 'EnmapPathError',
+ `The value for key "${key}" is not of type "${type.join(
+ '" or "',
+ )}" in the enmap "${this.#name}" (value was of type "${
+ this.get(key).constructor.name
+ }")`,
+ 'EnmapTypeError',
);
- return this.remove(key, val, path);
+ }
}
- /**
- * Returns whether or not the property exists within an object or array value in enmap.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use has() instead!
- * @param {string} key Required. The key of the element to check in the Enmap or array.
- * @param {*} path Required. The property to verify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
- * @return {boolean} Whether the property exists.
- * @deprecated Will be removed in Enmap 6! Use has() instead!
+ /*
+ * INTERNAL method to execute a mathematical operation. Cuz... javascript.
+ * And I didn't want to import mathjs!
+ * @param {number} base the lefthand operand.
+ * @param {string} op the operation.
+ * @param {number} opand the righthand operand.
+ * @return {number} the result.
*/
- hasProp(key, path) {
- process.emitWarning(
- 'ENMAP DEPRECATION hasProp() will be removed in the next major Enmap release (v6)! Please use has(key, path) instead.',
- );
- this.#readyCheck();
- this.#fetchCheck(key);
- if (isNil(path))
+ #math(base, op, opand) {
+ if (base == undefined || op == undefined || opand == undefined)
throw new Err(
- `No path provided to check for a property in "${key}" of enmap "${
- this.#name
- }"`,
- 'EnmapPathError',
+ 'Math Operation requires base and operation',
+ 'EnmapTypeError',
);
- return this.has(key, path);
- }
-
- /**
- * Searches for the existence of a single item where its specified property's value is identical to the given value
- * (`item[prop] === value`).
- * Do not use this to check for an item by its ID. Instead, use `enmap.has(id)`. See
- * [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) for details.
- * DEPRECATION WILL BE REMOVED IN ENMAP 6! Use has("key", "path") instead!
- * @param {string} prop The property to test against
- * @param {*} value The expected value
- * @returns {boolean}
- * @example
- * if (enmap.exists('username', 'Bob')) {
- * console.log('user here!');
- * }
- * @deprecated Will be removed in Enmap 6! Use has(key, path) instead!
- */
- exists(prop, value) {
- process.emitWarning(
- 'ENMAP DEPRECATION exists() will be removed in the next major Enmap release (v6)! Please use has(key, path) instead.',
- );
- this.#readyCheck();
- return Boolean(this.find(prop, value));
+ switch (op) {
+ case 'add':
+ case 'addition':
+ case '+':
+ return base + opand;
+ case 'sub':
+ case 'subtract':
+ case '-':
+ return base - opand;
+ case 'mult':
+ case 'multiply':
+ case '*':
+ return base * opand;
+ case 'div':
+ case 'divide':
+ case '/':
+ return base / opand;
+ case 'exp':
+ case 'exponent':
+ case '^':
+ return Math.pow(base, opand);
+ case 'mod':
+ case 'modulo':
+ case '%':
+ return base % opand;
+ case 'rand':
+ case 'random':
+ return Math.floor(Math.random() * Math.floor(opand));
+ }
+ return null;
}
-
- /* END DEPRECATED METHODS */
}
-module.exports = Enmap;
-
-/**
- * @external forEach
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach}
- */
-
-/**
- * @external keys
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys}
- */
-
-/**
- * @external values
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values}
- */
+export default Enmap;
diff --git a/test/index.js b/test/index.js
index 4a2b347..b9ef120 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,11 +1,12 @@
-/* global describe, test, beforeEach, afterEach, expect */
-const Enmap = require('../');
+/* global describe, test, beforeEach, afterEach, expect, beforeAll */
+import Enmap from '../';
+const enmap = new Enmap({ inMemory: true });
describe('Standard Enmaps', () => {
- let enmap;
-
+ beforeAll(() => {
+ enmap.clear();
+ })
describe('Basic Enmap', () => {
- enmap = new Enmap('::memory::');
test('inserts primitive values', () => {
expect(enmap.set('simplevalue', 'this is a string')).not.toBe(null);
expect(enmap.set('boolean', true)).not.toBe(null);
@@ -13,6 +14,7 @@ describe('Standard Enmaps', () => {
expect(enmap.set('null', null)).not.toBe(null);
});
test('remembers primitive values', () => {
+ expect(enmap.length).toBe(4);
expect(enmap.get('simplevalue')).toBe('this is a string');
expect(enmap.get('boolean')).toBe(true);
expect(enmap.get('integer')).toBe(42);
@@ -26,15 +28,21 @@ describe('Standard Enmaps', () => {
enmap.dec('integer');
expect(enmap.get('integer')).toBe(47);
});
+ test('can see if a value exists', () => {
+ expect(enmap.has('simplevalue')).toBe(true);
+ expect(enmap.has('nonexistent')).toBe(false);
+ });
+ test('can delete values', () => {
+ enmap.delete('simplevalue');
+ expect(enmap.get('simplevalue')).toBe(null);
+ });
test('can be cleared', () => {
enmap.clear();
- expect(enmap.size).toBe(0);
+ expect(enmap.length).toBe(0);
});
});
describe('Advanced Data Types', () => {
- enmap = new Enmap('::memory::');
-
test('supports arrays', () => {
expect(enmap.set('array', [1, 2, 3])).not.toBe(null);
expect(enmap.get('array').length).toBe(3);
@@ -88,26 +96,36 @@ describe('Standard Enmaps', () => {
date: new Date('Thu, 28 Apr 2016 22:02:17 GMT'),
map: new Map([['hello', 'world']]),
set: new Set([123, 456]),
- fn: function echo(arg) { return arg; },
re: /([^\s]+)/g,
// eslint-disable-next-line no-undef
big: BigInt(10),
});
expect(enmap.get('serialized', 'undef')).toBeUndefined();
- expect(enmap.get('serialized', 'fn')('test')).toBe('test');
expect(enmap.get('serialized', 'map').get('hello')).toBe('world');
});
+
+ test('deleting data', () => {
+ enmap.delete('serialized', 'str');
+ expect(enmap.get('serialized', 'str')).toBeUndefined();
+ enmap.delete('serialized', 'obj.foo');
+ expect(enmap.get('serialized', 'obj.foo')).toBeUndefined();
+ enmap.delete('serialized');
+ expect(enmap.get('serialized')).toBeNull();
+ });
});
});
describe('Advanced Data Type Methods', () => {
- let enmap;
+ beforeAll(() => {
+ enmap.clear();
+ });
beforeEach(() => {
- enmap = new Enmap('::memory::');
+ enmap.clear();
enmap.set('obj1', {
prop: 'prop',
foo: 'bar',
sub: { value: 'subvalue' },
+ arr: [1, 2, 3],
});
enmap.set('obj2', {
prop: 'prop',
@@ -117,87 +135,168 @@ describe('Advanced Data Type Methods', () => {
enmap.set('arr1', ['one', 'two', 3, 4]);
});
- test('can findAll using both properties and path', () => {
- expect(enmap.findAll('prop', 'prop').length).toBe(2);
- expect(enmap.findAll('sub.value', 'subvalue').length).toBe(2);
+ test('can filter using both properties and path', () => {
+ expect(enmap.filter('prop', 'prop').length).toBe(2);
+ expect(enmap.filter('sub.value', 'subvalue').length).toBe(2);
+ expect(enmap.filter(value => value?.foo?.includes('pha')).length).toBe(1);
});
test('can find using both properties and path', () => {
- // expect(enmap.find('prop', 'prop')).toEqual({
- // prop: 'prop',
- // foo: 'bar',
- // sub: { value: 'subvalue' }
- // });
expect(enmap.find('sub.value', 'subvalue')).toEqual({
prop: 'prop',
foo: 'bar',
sub: { value: 'subvalue' },
+ arr: [1, 2, 3],
});
});
+
+ test('can check if value includes', () => {
+ expect(enmap.includes('arr1', 'one')).toBe(true);
+ expect(enmap.includes('arr1', 3)).toBe(true);
+ expect(enmap.includes('arr1', 'three')).toBe(false);
+ expect(enmap.includes('obj1', 2, 'arr')).toBe(true);
+ });
+
+ test('can iterate over truthy/falsey predicates', () => {
+ enmap.delete('arr1');
+ enmap.set('obj3', { prop: 'prop' });
+ expect(enmap.some('prop', 'prop')).toBe(true);
+ expect(enmap.some('notprop', 'prop')).toBe(false);
+ expect(enmap.every('prop', 'prop')).toBe(true);
+ expect(enmap.every('notprop', 'prop')).toBe(false);
+ expect(enmap.some(value => value?.prop === 'prop')).toBe(true);
+ expect(enmap.some(value => value?.prop === 'notprop')).toBe(false);
+ expect(enmap.every(value => value?.prop === 'prop')).toBe(true);
+ expect(enmap.every(value => value?.prop === 'notprop')).toBe(false);
+ });
+
+ test('can map over values', () => {
+ enmap.delete('arr1');
+ const mapped = enmap.map(value => value.prop);
+ expect(mapped).toEqual(['prop', 'prop']);
+ const manualMapped = enmap.map('prop');
+ expect(manualMapped).toEqual(['prop', 'prop']);
+ expect(enmap.map('sub.value')).toEqual(['subvalue', 'subvalue']);
+ });
+
+ test('can reduce over values', () => {
+ const reduced = enmap.reduce((acc, value) => value.prop ? acc + value.prop : acc, '');
+ expect(reduced).toBe('propprop');
+ });
});
-describe('Basic Enmap Options', () => {
- let enmap;
- let baseObj;
- beforeEach(() => {
- baseObj = {
- prop1: false,
- prop2: 'thing',
- prop3: [1, 2, 3],
- obj: { thing: 'amajig' },
+describe('partition', () => {
+ beforeAll(() => {
+ enmap.clear();
+ for (let i = 0; i < 10; i++) {
+ enmap.set(`${i}`, {
+ number: i,
+ isEven: i % 2 === 0,
+ });
};
});
- afterEach(() => {
+ test('partition by function', () => {
+ const partitioned = enmap.partition((value) => {
+ return value.number % 2 === 0;
+ });
+ expect(partitioned[0].length).toBe(5);
+ expect(partitioned[1].length).toBe(5);
+ });
+
+ test('partition by property', () => {
+ const partitioned = enmap.partition('isEven', true);
+ expect(partitioned[0].length).toBe(5);
+ expect(partitioned[1].length).toBe(5);
+ });
+});
+
+describe('Enmap Observables', () => {
+ // This entire set of test was written by Copilot. What do you think?
+
+ beforeAll(() => {
enmap.clear();
- enmap = null;
+ enmap.set('test', {
+ a: 1,
+ b: 2,
+ c: 3,
+ d: [1, 2, 3, 4],
+ e: { a: 'a', b: 'b', c: 'c' },
+ });
+ });
+
+ test('can observe a value', () => {
+ const obj = enmap.observe('test');
+ expect(obj.a).toBe(1);
+ obj.a = 2;
+ expect(enmap.get('test', 'a')).toBe(2);
+ });
+
+ test('can observe a subproperty', () => {
+ const obj = enmap.observe('test', 'e');
+ expect(obj.a).toBe('a');
+ obj.a = 'b';
+ expect(enmap.get('test', 'e.a')).toBe('b');
});
- test('supports direct passing by reference (cloneLevel none)', () => {
- enmap = new Enmap({ name: '::memory::', cloneLevel: 'none' });
- enmap.set('foo', baseObj);
- enmap.set('foo', 'other', 'prop2');
- enmap.push('foo', 4, 'prop3');
- // by reference modifies object properties at any level.
- expect(baseObj.prop2).toBe('other');
- expect(baseObj.prop3.length).toBe(4);
+ test('can observe an array', () => {
+ const arr = enmap.observe('test', 'd');
+ expect(arr.length).toBe(4);
+ arr.push(5);
+ expect(enmap.get('test', 'd').length).toBe(5);
});
- test('supports shallow clones', () => {
- enmap = new Enmap({ name: '::memory::', cloneLevel: 'shallow' });
- enmap.set('foo', baseObj);
- enmap.set('foo', 'other', 'prop2');
- enmap.push('foo', 4, 'prop3');
- // shallow clones do not allow base props to change in referenced object
- expect(baseObj.prop2).toBe('thing');
- // shallow clones still allow subprops to be modified, though.
- expect(baseObj.prop3.length).toBe(4);
+ test('can observe a subproperty of an array', () => {
+ const arr = enmap.observe('test', 'd');
+ const obj = arr[0];
+ expect(obj).toBe(1);
+ arr[0] = 2;
+ expect(enmap.get('test', 'd.0')).toBe(2);
});
+});
- test('supports deep clones', () => {
- enmap = new Enmap({ name: '::memory::', cloneLevel: 'deep' });
- enmap.set('foo', baseObj);
- enmap.set('foo', 'other', 'prop2');
- enmap.push('foo', 4, 'prop3');
- // deep clones do not allow base props to change in referenced object
- expect(baseObj.prop2).toBe('thing');
- // deep clones do not allow sub props to be changed, either.
- expect(baseObj.prop3.length).toBe(3);
+describe('sweep', () => {
+ beforeEach(() => {
+ enmap.clear();
+ for (let i = 0; i < 10; i++) {
+ enmap.set(`${i}`, {
+ number: i,
+ isEven: i % 2 === 0,
+ });
+ };
+ });
+ test('sweep by function', () => {
+ enmap.sweep((value) => {
+ return value.number % 2 === 0;
+ });
+ expect(enmap.size).toBe(5);
});
+ test('sweep by property', () => {
+ enmap.sweep('isEven', false);
+ expect(enmap.size).toBe(5);
+ });
+});
+
+describe('Basic Enmap Options', () => {
+ const localEnmap = new Enmap({ inMemory : true, ensureProps: true });
test('supports deep ensure() merge', () => {
- enmap = new Enmap({ name: '::memory::', ensureProps: true });
const defaultValue = {
foo: 'bar',
bar: { foo: 1 },
};
- enmap.set('obj', {});
- enmap.ensure('obj', defaultValue);
- expect(enmap.get('obj', 'bar.foo')).toBe(1);
+ localEnmap.set('obj', {});
+ localEnmap.ensure('obj', defaultValue);
+ expect(localEnmap.get('obj', 'bar.foo')).toBe(1);
});
});
describe('Enmap Advanced Options', () => {
+ class ExampleClass {
+ constructor(id) {
+ this.id = id;
+ }
+ }
let enmap;
const defaultData = {
a: 1,
@@ -207,13 +306,14 @@ describe('Enmap Advanced Options', () => {
e: { a: 'a', b: 'b', c: 'c' },
};
afterEach(() => {
- enmap.close();
+ enmap.clear();
enmap = null;
});
+
test('supports autoEnsure', () => {
- enmap = new Enmap({ name: '::memory::', autoEnsure: defaultData });
+ enmap = new Enmap({ inMemory: true, autoEnsure: defaultData });
expect(enmap.get('test')).toEqual(defaultData);
- expect(enmap.size).toBe(1);
+ expect(enmap.length).toBe(1);
enmap.set('test', 'a', 'a');
expect(enmap.get('test')).toEqual({
...defaultData,
@@ -227,15 +327,18 @@ describe('Enmap Advanced Options', () => {
});
test('supports serializers', () => {
+ defaultData.c = new ExampleClass(3);
enmap = new Enmap({
- name: '::memory::',
- serializer: (data, key) => ({
+ inMemory: true,
+ serializer: (data) => ({
...data,
a: 'modified',
+ c: data.c.id,
}),
- deserializer: (data, key) => ({
+ deserializer: (data) => ({
...data,
a: 1,
+ c: new ExampleClass(data.c),
}),
});
enmap.set('test', defaultData);
@@ -243,6 +346,6 @@ describe('Enmap Advanced Options', () => {
const data = enmap.db
.prepare(`SELECT * FROM 'MemoryEnmap' WHERE key = ?;`)
.get('test');
- expect(data.value).toBe('{"a":"modified","b":2,"c":3,"d":[1,2,3,4],"e":{"a":"a","b":"b","c":"c"}}');
+ expect(data.value).toBe('{"t":0,"v":{"a":{"t":1,"v":"modified"},"b":{"t":2,"v":2},"c":{"t":2,"v":3},"d":{"t":4,"v":[{"t":2,"v":1},{"t":2,"v":2},{"t":2,"v":3},{"t":2,"v":4}]},"e":{"t":0,"v":{"a":{"t":1,"v":"a"},"b":{"t":1,"v":"b"},"c":{"t":1,"v":"c"}}}}}');
});
});
diff --git a/typings/index.d.ts b/typings/index.d.ts
index 145e3c7..377f9c1 100644
--- a/typings/index.d.ts
+++ b/typings/index.d.ts
@@ -184,7 +184,7 @@ declare module 'enmap' {
* @param val Required. The value of the element to add to The Enmap.
* If the Enmap is persistent this value MUST be stringifiable as JSON.
* @param path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* // Settings Properties
* enmap.set('IhazObjects', 'color', 'blue'); //modified previous object
@@ -211,7 +211,7 @@ declare module 'enmap' {
* Retrieves a key from the enmap. If fetchAll is false, returns a promise.
* @param key The key to retrieve from the enmap.
* @param path Optional. The property to retrieve from the object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* const myKeyValue = enmap.get("myKey");
* console.log(myKeyValue);
@@ -278,7 +278,7 @@ declare module 'enmap' {
* @param key Required. The key of the element to add to The Enmap or array.
* This value MUST be a string or number.
* @param path Required. The property to modify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @param val Required. The value to apply to the specified property.
* @returns The enmap.
*/
@@ -290,7 +290,7 @@ declare module 'enmap' {
* This value MUST be a string or number.
* @param val Required. The value to push to the array.
* @param path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @param allowDupes Optional. Allow duplicate values in the array (default: false).
* @example
* // Assuming
@@ -308,7 +308,7 @@ declare module 'enmap' {
* @param key Required. The key of the element.
* This value MUST be a string or number.
* @param path Required. The name of the array property to push to.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @param val Required. The value push to the array property.
* @param allowDupes Allow duplicate values in the array (default: false).
* @returns The enmap.
@@ -377,7 +377,7 @@ declare module 'enmap' {
* Returns the specific property within a stored value. If the key does not exist or the value is not an object, throws an error.
* @param key Required. The key of the element to get from The Enmap.
* @param path Required. The property to retrieve from the object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @return The value of the property obtained.
*/
public getProp(key: K, path: string): any;
@@ -388,7 +388,7 @@ declare module 'enmap' {
* @param key Required. The key you want to make sure exists.
* @param defaultValue Required. The value you want to save in the database and return as default.
* @param path 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"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* // Simply ensure the data exists (for using property methods):
* enmap.ensure("mykey", {some: "value", here: "as an example"});
@@ -409,7 +409,7 @@ declare module 'enmap' {
* @param key Required. The key of the element to add to The Enmap or array.
* This value MUST be a string or number.
* @param path Optional. The property to verify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @example
* if(enmap.has("myKey")) {
* // key is there
@@ -423,7 +423,7 @@ declare module 'enmap' {
* Returns whether or not the property exists within an object or array value in enmap.
* @param key Required. The key of the element to check in the Enmap or array.
* @param path Required. The property to verify inside the value object or array.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @return Whether the property exists.
*/
public hasProp(key: K, path: string): boolean;
@@ -433,7 +433,7 @@ declare module 'enmap' {
* Note: Does not return a boolean, unlike the standard Map.
* @param key Required. The key of the element to delete from The Enmap.
* @param path Optional. The name of the property to remove from the object.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @returns The enmap.
*/
public delete(key: K, path?: string): this;
@@ -442,7 +442,7 @@ declare module 'enmap' {
* Delete a property from an object or array value in Enmap.
* @param key Required. The key of the element to delete the property from in Enmap.
* @param path Required. The name of the property to remove from the object.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
*/
public deleteProp(key: K, path: string): void;
@@ -460,7 +460,7 @@ declare module 'enmap' {
* This value MUST be a string or number.
* @param val Required. The value to remove from the array or object.
* @param path Optional. The name of the array property to remove from.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3".
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3".
* If not presents, removes directly from the value.
* @returns The enmap.
*/
@@ -472,7 +472,7 @@ declare module 'enmap' {
* @param key Required. The key of the element.
* This value MUST be a string or number.
* @param path Required. The name of the array property to remove from.
- * Can be a path with dot notation, such as "prop1.subprop2.subprop3"
+ * Should be a path with dot notation, such as "prop1.subprop2.subprop3"
* @param val Required. The value to remove from the array property.
* @returns The enmap.
*/