apiro-db 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +107 -103
- package/lib/path.js +35 -0
- package/lib/store.js +52 -11
- package/package.json +18 -18
package/LICENSE
CHANGED
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,103 +1,107 @@
|
|
|
1
|
-
# Apiro DB
|
|
2
|
-
|
|
3
|
-
A lightweight, zero-dependency, encrypted data store for Node.js.
|
|
4
|
-
|
|
5
|
-
No configuration. No secrets. Just import and use.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
* Zero dependencies
|
|
12
|
-
* Fully asynchronous API
|
|
13
|
-
* AES-256-GCM encrypted storage
|
|
14
|
-
* Automatic key generation
|
|
15
|
-
* Machine-bound encryption
|
|
16
|
-
* Human-unreadable data at rest
|
|
17
|
-
* Simple, familiar database methods
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Installation
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install apiro-db
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Usage
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
import { SecureStore } from "apiro-db";
|
|
33
|
-
|
|
34
|
-
const db = new SecureStore({
|
|
35
|
-
file: "./data.db", // Optional
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
await db.set("coins", 100);
|
|
39
|
-
await db.add("coins", 25);
|
|
40
|
-
await db.push("items", "shield");
|
|
41
|
-
|
|
42
|
-
console.log(await db.get("coins")); // 125
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Configuration
|
|
48
|
-
|
|
49
|
-
| Option | Type | Required | Description |
|
|
50
|
-
| -------- | ------ | -------- | ----------------------------------- |
|
|
51
|
-
| `file` | string | No | Path to the encrypted data file |
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## API Reference
|
|
56
|
-
|
|
57
|
-
### `get(key)`
|
|
58
|
-
|
|
59
|
-
Returns the value stored under the key.
|
|
60
|
-
|
|
61
|
-
### `set(key, value)`
|
|
62
|
-
|
|
63
|
-
Sets a value for the key.
|
|
64
|
-
|
|
65
|
-
### `delete(key)`
|
|
66
|
-
|
|
67
|
-
Deletes a key and returns `true` if it existed.
|
|
68
|
-
|
|
69
|
-
### `add(key, number)`
|
|
70
|
-
|
|
71
|
-
Adds a number to an existing numeric value.
|
|
72
|
-
|
|
73
|
-
### `subtract(key, number)`
|
|
74
|
-
|
|
75
|
-
Subtracts a number from a value.
|
|
76
|
-
|
|
77
|
-
### `push(key, value)`
|
|
78
|
-
|
|
79
|
-
Pushes a value onto an array.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
*
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
1
|
+
# Apiro DB
|
|
2
|
+
|
|
3
|
+
A lightweight, zero-dependency, encrypted data store for Node.js.
|
|
4
|
+
|
|
5
|
+
No configuration. No secrets. Just import and use.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
* Zero dependencies
|
|
12
|
+
* Fully asynchronous API
|
|
13
|
+
* AES-256-GCM encrypted storage
|
|
14
|
+
* Automatic key generation
|
|
15
|
+
* Machine-bound encryption
|
|
16
|
+
* Human-unreadable data at rest
|
|
17
|
+
* Simple, familiar database methods
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install apiro-db
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { SecureStore } from "apiro-db";
|
|
33
|
+
|
|
34
|
+
const db = new SecureStore({
|
|
35
|
+
file: "./data.db", // Optional
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await db.set("coins", 100);
|
|
39
|
+
await db.add("coins", 25);
|
|
40
|
+
await db.push("items", "shield");
|
|
41
|
+
|
|
42
|
+
console.log(await db.get("coins")); // 125
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
| Option | Type | Required | Description |
|
|
50
|
+
| -------- | ------ | -------- | ----------------------------------- |
|
|
51
|
+
| `file` | string | No | Path to the encrypted data file |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## API Reference
|
|
56
|
+
|
|
57
|
+
### `get(key)`
|
|
58
|
+
|
|
59
|
+
Returns the value stored under the key.
|
|
60
|
+
|
|
61
|
+
### `set(key, value)`
|
|
62
|
+
|
|
63
|
+
Sets a value for the key.
|
|
64
|
+
|
|
65
|
+
### `delete(key)`
|
|
66
|
+
|
|
67
|
+
Deletes a key and returns `true` if it existed.
|
|
68
|
+
|
|
69
|
+
### `add(key, number)`
|
|
70
|
+
|
|
71
|
+
Adds a number to an existing numeric value.
|
|
72
|
+
|
|
73
|
+
### `subtract(key, number)`
|
|
74
|
+
|
|
75
|
+
Subtracts a number from a value.
|
|
76
|
+
|
|
77
|
+
### `push(key, value)`
|
|
78
|
+
|
|
79
|
+
Pushes a value onto an array.
|
|
80
|
+
|
|
81
|
+
### `has(key, value) or has(key)`
|
|
82
|
+
|
|
83
|
+
Checks for a value in a directory, or for the directory itself.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Security
|
|
88
|
+
|
|
89
|
+
* Uses AES-256-GCM authenticated encryption
|
|
90
|
+
* Key derivation via `scrypt`
|
|
91
|
+
* Tamper detection built in
|
|
92
|
+
* No plaintext data stored on disk
|
|
93
|
+
* No external crypto dependencies
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Limitations
|
|
98
|
+
|
|
99
|
+
* Single-file storage
|
|
100
|
+
* Not designed for concurrent multi-process writes
|
|
101
|
+
* Entire store is encrypted as a single unit
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
package/lib/path.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
function getByPath(obj, path) {
|
|
2
|
+
return path.split(".").reduce((o, k) => (o ? o[k] : undefined), obj);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function setByPath(obj, path, value) {
|
|
6
|
+
const keys = path.split(".");
|
|
7
|
+
let curr = obj;
|
|
8
|
+
|
|
9
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
10
|
+
if (typeof curr[keys[i]] !== "object" || curr[keys[i]] === null) {
|
|
11
|
+
curr[keys[i]] = {};
|
|
12
|
+
}
|
|
13
|
+
curr = curr[keys[i]];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
curr[keys[keys.length - 1]] = value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function deleteByPath(obj, path) {
|
|
20
|
+
const keys = path.split(".");
|
|
21
|
+
let curr = obj;
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
24
|
+
if (!curr[keys[i]]) return false;
|
|
25
|
+
curr = curr[keys[i]];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return delete curr[keys[keys.length - 1]];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
getByPath,
|
|
33
|
+
setByPath,
|
|
34
|
+
deleteByPath
|
|
35
|
+
};
|
package/lib/store.js
CHANGED
|
@@ -2,6 +2,11 @@ const crypto = require("crypto");
|
|
|
2
2
|
const { readFile, writeFile } = require("./file");
|
|
3
3
|
const { encrypt, decrypt } = require("./crypto");
|
|
4
4
|
const { generateMasterKey, encryptMasterKey, decryptMasterKey } = require("./masterKey");
|
|
5
|
+
const {
|
|
6
|
+
getByPath,
|
|
7
|
+
setByPath,
|
|
8
|
+
deleteByPath
|
|
9
|
+
} = require("./path");
|
|
5
10
|
|
|
6
11
|
class SecureStore {
|
|
7
12
|
constructor(options = {}) {
|
|
@@ -45,43 +50,79 @@ class SecureStore {
|
|
|
45
50
|
|
|
46
51
|
async get(key) {
|
|
47
52
|
await this.ready;
|
|
48
|
-
return this.data
|
|
53
|
+
return getByPath(this.data, key);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
async set(key, value) {
|
|
52
57
|
await this.ready;
|
|
53
|
-
this.data
|
|
58
|
+
setByPath(this.data, key, value);
|
|
54
59
|
await this._save();
|
|
55
60
|
return value;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
async delete(key) {
|
|
59
64
|
await this.ready;
|
|
60
|
-
const existed =
|
|
61
|
-
delete this.data[key];
|
|
65
|
+
const existed = deleteByPath(this.data, key);
|
|
62
66
|
await this._save();
|
|
63
67
|
return existed;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
async add(key, amount) {
|
|
67
71
|
await this.ready;
|
|
68
|
-
|
|
69
|
-
this.data
|
|
72
|
+
|
|
73
|
+
let current = getByPath(this.data, key);
|
|
74
|
+
if (typeof current !== "number") current = 0;
|
|
75
|
+
|
|
76
|
+
const next = current + amount;
|
|
77
|
+
setByPath(this.data, key, next);
|
|
78
|
+
|
|
70
79
|
await this._save();
|
|
71
|
-
return
|
|
80
|
+
return next;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
async subtract(key, amount) {
|
|
75
|
-
|
|
84
|
+
await this.ready;
|
|
85
|
+
|
|
86
|
+
let current = getByPath(this.data, key);
|
|
87
|
+
if (typeof current !== "number") current = 0;
|
|
88
|
+
|
|
89
|
+
const next = current - amount;
|
|
90
|
+
setByPath(this.data, key, next);
|
|
91
|
+
|
|
92
|
+
await this._save();
|
|
93
|
+
return next;
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
async push(key, value) {
|
|
79
97
|
await this.ready;
|
|
80
|
-
|
|
81
|
-
this.data
|
|
98
|
+
|
|
99
|
+
let arr = getByPath(this.data, key);
|
|
100
|
+
if (!Array.isArray(arr)) arr = [];
|
|
101
|
+
|
|
102
|
+
arr.push(value);
|
|
103
|
+
setByPath(this.data, key, arr);
|
|
104
|
+
|
|
82
105
|
await this._save();
|
|
83
|
-
return
|
|
106
|
+
return arr;
|
|
84
107
|
}
|
|
108
|
+
|
|
109
|
+
async has(path, value) {
|
|
110
|
+
await this.ready;
|
|
111
|
+
|
|
112
|
+
const current = getByPath(this.data, path);
|
|
113
|
+
|
|
114
|
+
if (current === undefined) return false;
|
|
115
|
+
|
|
116
|
+
if (value === undefined) return true;
|
|
117
|
+
|
|
118
|
+
// If current is an array, check includes
|
|
119
|
+
if (Array.isArray(current)) {
|
|
120
|
+
return current.includes(value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return current === value;
|
|
124
|
+
}
|
|
125
|
+
|
|
85
126
|
}
|
|
86
127
|
|
|
87
128
|
module.exports = { SecureStore };
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "apiro-db",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "A lightweight, zero-dependency, encrypted data store for Node.js.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
-
},
|
|
9
|
-
"keywords": ["database", "storage", "encryption", "secure"],
|
|
10
|
-
"author": "krispowers",
|
|
11
|
-
"license": "ISC",
|
|
12
|
-
"files": [
|
|
13
|
-
"Readme.md",
|
|
14
|
-
"index.js",
|
|
15
|
-
"lib/",
|
|
16
|
-
"LICENSE"
|
|
17
|
-
]
|
|
18
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "apiro-db",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "A lightweight, zero-dependency, encrypted data store for Node.js.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": ["database", "storage", "encryption", "secure"],
|
|
10
|
+
"author": "krispowers",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"files": [
|
|
13
|
+
"Readme.md",
|
|
14
|
+
"index.js",
|
|
15
|
+
"lib/",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
]
|
|
18
|
+
}
|