snowbase 5.0.0 → 6.0.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ ## [6.0.0] - 2026-07-XX
4
+
5
+ ### Added
6
+
7
+ - Added a cache layer to avoid repeated reads from disk and improve response times.
8
+ - Added cache validation based on file modification time and file size.
9
+ - Added support for a custom backup directory.
10
+ - Added an async version of the database for non-blocking operations.
11
+ - Added optional encryption support for stored data.
12
+ - Added backup versioning with filenames following the pattern backup_YYYY-MM-DD_HH-MM-SS-XXXXX.json.
13
+ - Added a CLI with commands for init, inspect, backup, restore, clear, get, set, remove, and has.
14
+ - Added richer database operations such as set, save, get, remove, clear, all, has, count, and find.
15
+ - Added event-based lifecycle support with ready and error events.
16
+ - Added operation events for get, set, remove, clear, writes, and backup creation.
17
+
18
+ ### Changed
19
+
20
+ - Reworked the storage layer to support persistence, backup, logging, and encryption in a more structured way.
21
+ - Improved initialization so the base directory, backup directory, log directory, and database file are created automatically when missing.
22
+ - Standardized backup and logging behavior to be configurable through constructor options.
23
+ - The keys are now saved in alphabetic order.
24
+
25
+ ### Fixed
26
+
27
+ - Fixed issues that caused repeated file reads when the database directory changed or the file state was updated.
28
+ - Fixed backup handling to keep the backup folder organized and avoid excessive backup accumulation.
29
+ - Improved validation to reject invalid database root structures.
30
+
31
+ ### Performance Improvements
32
+
33
+ - Reduced unnecessary disk I/O by reusing cached data until the underlying file changes.
34
+ - Improved write and backup flow for faster and more predictable persistence operations.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright © 2026 Pedro Henrique Brandão (p3droob)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,31 +1,224 @@
1
- # Snowbase ❄️
2
-
3
- ## Overview
4
- - #### [Alpha Discord](https://www.npmjs.com/package/alpha-discord) - A library for interact with discord [API](https://discord.com/api) on js
5
- - ## Server
6
- ```js
7
- const snowbase = require('snowbase');
8
-
9
- const db = new snowbase.Server({
10
- login: 'Your account login of snowbase',
11
- password: 'Your password'
12
- });
13
- db.save('value1', 150)//emits valueUpdate event: key, {old: old_value, new: new_value}
14
-
15
- db.get('value1')//150
16
-
17
- db.remove('value1')//emits valueDelete event: key, deleted_value
18
- ```
19
-
20
- - ## Local
21
- ```js
22
- const db = new snowbase.Local('./database.json')//or other file
23
-
24
- db.save('value2', 100)
25
-
26
- db.get('value2')//100
27
-
28
- db.all(a => a == 100)//filter
29
-
30
- db.remove('value2')
31
- ```
1
+ <div align="center">
2
+ <h1>Snowbase ❄️</h1>
3
+ <br>
4
+ <img src="./img/snowbase-logo.png" alt="Snowbase logo" width="200" height="200" style="border-radius:35%;border: 4px solid #e7e9ebff;"/>
5
+ <p>A lightweight file-based database for Node.js</p>
6
+ <a href="https://www.npmjs.com/package/snowbase">
7
+ <img src="https://img.shields.io/npm/v/snowbase?color=blue" alt="Current version"/>
8
+ </a>
9
+ <a href="https://www.npmjs.com/package/snowbase">
10
+ <img
11
+ src="https://packagephobia.com/badge?p=snowbase@6.0.0"
12
+ alt="install size">
13
+ </a>
14
+ <a href="https://www.npmjs.com/package/snowbase">
15
+ <img src="https://img.shields.io/npm/dt/snowbase?color=darkcyan" alt="npm downloads"/>
16
+ </a>
17
+ <img src="https://img.shields.io/npm/l/snowbase" alt="license"/>
18
+ <br>
19
+ <b>Simple.</b> <b>Fast.</b> <b>Persistent.</b>
20
+ </div>
21
+
22
+ ---
23
+
24
+ - [Overview](#overview)
25
+ - [Features](#features)
26
+ - [Installation](#installation)
27
+ - [Usage](#usage)
28
+ - [Configuration](#configuration)
29
+ - [Events](#events)
30
+ - [Async API](#async-api)
31
+ - [CLI](#cli)
32
+ - [License](#license)
33
+
34
+ ## Overview
35
+
36
+ Snowbase is a simple, fast, and persistent file-based database for Node.js.
37
+ It creates and manages a local JSON file to store your data safely, making it perfect for lightweight applications, prototypes, scripts, and small tools.
38
+
39
+ No servers. No complex setup. Just storage.
40
+
41
+ ## ✨ Features
42
+
43
+ - ⚡ Lightweight and easy to use
44
+ - 📦 File-based persistence with no external database required
45
+ - 🔐 Optional encryption for stored data
46
+ - 🧾 Optional logging for operations
47
+ - 💾 Automatic backups with versioned files
48
+ - ⚙️ Supports both sync and async APIs
49
+ - 🧩 Works well for prototypes, local apps, and small projects
50
+
51
+ ## 📥 Installation
52
+
53
+ ```bash
54
+ npm install snowbase
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ### CommonJS
60
+
61
+ ```js
62
+ const Snowbase = require("snowbase");
63
+
64
+ const db = new Snowbase();
65
+
66
+ db.on("ready", () => {
67
+ console.log("Database is ready");
68
+ });
69
+ ```
70
+
71
+ ### ES Modules
72
+
73
+ ```js
74
+ import Snowbase from "snowbase";
75
+
76
+ const db = new Snowbase();
77
+
78
+ db.on("ready", () => {
79
+ console.log("Database is ready");
80
+ });
81
+ ```
82
+
83
+ ### Basic operations
84
+
85
+ ```js
86
+ // Set a value
87
+ db.set("user/name", "Pedro");
88
+
89
+ // Get a value
90
+ console.log(db.get("user/name")); // Pedro
91
+
92
+ // Remove a value
93
+ db.remove("user/name");
94
+
95
+ // Clear the whole database
96
+ db.clear();
97
+ ```
98
+
99
+ ## Configuration
100
+
101
+ You can configure Snowbase during initialization:
102
+
103
+ ```js
104
+ const db = new Snowbase({
105
+ baseDir: "./my-database",
106
+ encryption: true,
107
+ secret: "my-secret",
108
+ logging: true,
109
+ backup: true,
110
+ });
111
+ ```
112
+
113
+ ### Options
114
+
115
+ - baseDir: directory where the database file will be stored
116
+ - encryption: enables encrypted persistence
117
+ - secret: secret key used when encryption is enabled
118
+ - logging: enables operation logging
119
+ - backup: enables automatic backups
120
+
121
+ ## Advanced examples
122
+
123
+ ### Set and save nested data
124
+
125
+ ```js
126
+ db.set("patients", {
127
+ john: {
128
+ age: 20,
129
+ name: "John",
130
+ city: "New York",
131
+ },
132
+ albert: {
133
+ age: 31,
134
+ name: "Albert",
135
+ city: "Chicago",
136
+ },
137
+ });
138
+ ```
139
+
140
+ ### Read and query data
141
+
142
+ ```js
143
+ console.log(db.get("patients/john/city")); // New York
144
+ console.log(db.has("patients/albert")); // true
145
+ console.log(db.count("patients")); // 2
146
+ ```
147
+
148
+ ### Find and remove
149
+
150
+ ```js
151
+ const result = db.find("patients", ({ key }) => key.includes("a"));
152
+ console.log(result);
153
+
154
+ db.remove("patients/albert"); // true
155
+ db.remove("patients/susy"); // false
156
+ ```
157
+
158
+ ## Events
159
+
160
+ Snowbase can emit events for lifecycle and database operations. You can listen to them with the standard EventEmitter API.
161
+
162
+ ```js
163
+ const db = new Snowbase();
164
+
165
+ db.on("ready", () => {
166
+ console.log("Database is ready");
167
+ });
168
+
169
+ db.on("error", (err) => {
170
+ console.error(err.message);
171
+ });
172
+
173
+ db.on("get", (details, timestamp) => {
174
+ console.log("Get event", details, timestamp);
175
+ });
176
+ ```
177
+
178
+ ### Available events
179
+
180
+ - ready: emitted when the database instance is initialized
181
+ - error: emitted when an error occurs; receives an Error object
182
+ - get: emitted after a read operation; payload includes the path and the resolved value
183
+ - set: emitted after a write operation; payload includes the path, key, and value
184
+ - remove: emitted after a remove operation; payload includes the path, removed value, and whether it was removed
185
+ - clear: emitted after clearing the database; payload includes the previous data
186
+ - \_write: emitted after data is written to disk
187
+ - create backup: emitted when a backup file is created
188
+
189
+ ## Async API
190
+
191
+ For non-blocking operations, use the async version:
192
+
193
+ ```js
194
+ import Snowbase from "snowbase/async";
195
+
196
+ const db = new Snowbase({
197
+ encryption: true,
198
+ secret: "my-secret",
199
+ backup: true,
200
+ });
201
+
202
+ await db.set("user/name", "Pedro");
203
+ console.log(await db.get("user/name"));
204
+ ```
205
+
206
+ ## CLI
207
+
208
+ Snowbase also includes a CLI for common tasks:
209
+
210
+ ```bash
211
+ snowbase init
212
+ snowbase inspect
213
+ snowbase backup
214
+ snowbase restore
215
+ snowbase get user/name
216
+ snowbase set user/name Pedro
217
+ snowbase remove user/name
218
+ snowbase clear
219
+ snowbase has
220
+ ```
221
+
222
+ ## License
223
+
224
+ MIT
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env node
2
+ import Snowbase from "../src/Snowbase.js";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import chalk from "chalk";
6
+
7
+ const args = process.argv.slice(2);
8
+ const cmd = args[0];
9
+ function levenshtein(a, b) {
10
+ const matrix = Array.from({ length: b.length + 1 }, (_, i) => [i]);
11
+
12
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
13
+
14
+ for (let i = 1; i <= b.length; i++) {
15
+ for (let j = 1; j <= a.length; j++) {
16
+ matrix[i][j] =
17
+ b[i - 1] === a[j - 1]
18
+ ? matrix[i - 1][j - 1]
19
+ : Math.min(
20
+ matrix[i - 1][j] + 1,
21
+ matrix[i][j - 1] + 1,
22
+ matrix[i - 1][j - 1] + 1,
23
+ );
24
+ }
25
+ }
26
+
27
+ return matrix[b.length][a.length];
28
+ }
29
+
30
+ function suggestCommand(input, commands) {
31
+ let best = null;
32
+ let distance = Infinity;
33
+
34
+ for (const command of commands) {
35
+ const d = levenshtein(input, command);
36
+
37
+ if (d < distance) {
38
+ distance = d;
39
+ best = command;
40
+ }
41
+ }
42
+
43
+ return distance <= 3 ? best : null;
44
+ }
45
+
46
+ let helpMessage = `
47
+ ${chalk.bold("SNOWBASE COMMANDS")}
48
+ ${chalk.blue("init")} Load the directory and create the database file
49
+
50
+ ${chalk.blue("inspect")} Inspect the database file
51
+
52
+ ${chalk.blue("backup")} Creates a backup of the database file
53
+
54
+ ${chalk.blue("restore")} Restore the database file
55
+
56
+ ${chalk.blue("clear")} Clear the database file
57
+
58
+ ============================================================
59
+ ${chalk.bold("COMPILER OPTION")}
60
+
61
+ ${chalk.blue("--baseDir")} The directory where the database is located. Default: ./snowbase
62
+
63
+ ${chalk.blue("--encryption")} Enables encryption. Default: false
64
+
65
+ ${chalk.blue("--secret")} The secret key for encryption. Default: ""
66
+
67
+ ${chalk.blue("--logging")} Enables logging. Default: false
68
+
69
+ ${chalk.blue("--backup")} Enables backup. Default: false
70
+
71
+ ${chalk.blue("--restoreFile")} The backup file to restore. Default: ""
72
+
73
+ ${chalk.bold("EXAMPLE")}
74
+ ${chalk.yellow('snowbase init --baseDir "./snowbase" --encryption --secret "mysecret" --logging --backup')}
75
+ `;
76
+ if (!cmd) {
77
+ console.log(helpMessage);
78
+ }
79
+
80
+ const commands = ["init", "inspect", "backup", "clear", "restore", "get", "set", "remove", "has"];
81
+ if (cmd) {
82
+ let config = {
83
+ baseDir: "./snowbase",
84
+ encryption: false,
85
+ secret: "",
86
+ logging: false,
87
+ backup: false,
88
+ };
89
+ for (let i = 1; i < args.length; i++) {
90
+ const arg = args[i];
91
+
92
+ if (!arg.startsWith("--")) continue;
93
+
94
+ const key = arg.slice(2);
95
+
96
+ if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
97
+ config[key] = args[++i];
98
+ } else {
99
+ config[key] = true;
100
+ }
101
+ }
102
+ const db = new Snowbase(config);
103
+
104
+ switch (cmd) {
105
+ default: {
106
+ console.log(helpMessage);
107
+ const suggestion = suggestCommand(cmd, commands);
108
+ if (suggestion) {
109
+ console.log(`Did you mean ${chalk.blue(suggestion)}?`);
110
+ }
111
+ break;
112
+ }
113
+
114
+ case "init": {
115
+ console.log(`Snowbase initialized at ${db.baseDir}`);
116
+ console.log(db.backupDir);
117
+ break;
118
+ }
119
+ case "inspect": {
120
+ const all = db.get();
121
+ let keys = Object.keys(all || {});
122
+ let d = 0;
123
+ if (
124
+ keys.length === 4 &&
125
+ keys.sort().every((v, i) => v === ["data", "iv", "salt", "tag"][i])
126
+ ) {
127
+ d = 1;
128
+ console.log(chalk.blue("Database is encrypted, wait..."));
129
+ try {
130
+ keys = Object.keys(db._decrypt(JSON.stringify(all), config.secret));
131
+ } catch (error) {
132
+ d = -1;
133
+ }
134
+ }
135
+ if (d === 1)
136
+ console.log(chalk.greenBright("Database decrypted successfully"));
137
+ if (d > -1) {
138
+ console.log(`Number of keys: ${keys.length}`);
139
+ console.log("Keys:", keys);
140
+ console.log("Size of database:", JSON.stringify(all).length, " bytes");
141
+ } else
142
+ console.log(
143
+ chalk.red("Decryption failed, please provide a valid secret"),
144
+ );
145
+ break;
146
+ }
147
+
148
+ case "backup": {
149
+ const backupFile = db.storage.createBackup();
150
+ if (!backupFile)
151
+ console.log(
152
+ chalk.red(
153
+ "You need to use --backup flag and must have a backup directory",
154
+ ),
155
+ );
156
+ else console.log(`Backup created at ${backupFile}`);
157
+ break;
158
+ }
159
+
160
+ case "restore": {
161
+ if (!config.restoreFile) {
162
+ console.log(
163
+ "Usage: snowbase restore --secret <secret, only if needed> --restoreFile <backup-ID>",
164
+ );
165
+ console.log(
166
+ "Example: snowbase restore --secret mysecret123 --restoreFile 2026-06-27T14-30-45-123Z-asf3k",
167
+ );
168
+ break;
169
+ }
170
+ const backupFile = path.join(
171
+ db.storage.backupDir || "./snowbase/backups",
172
+ `backup_${config.restoreFile}.json`,
173
+ );
174
+ if (fs.existsSync(backupFile)) {
175
+ let fileData = fs.readFileSync(backupFile, "utf8");
176
+ fileData = JSON.parse(fileData);
177
+ let keys = Object.keys(fileData || {});
178
+ let d = 0;
179
+ if (
180
+ keys.length === 4 &&
181
+ keys.sort().every((v, i) => v === ["data", "iv", "salt", "tag"][i])
182
+ ) {
183
+ d++;
184
+ console.log(chalk.blue("Database is encrypted, wait..."));
185
+ try {
186
+ fileData = db._decrypt(JSON.stringify(fileData), config.secret);
187
+ } catch (error) {
188
+ d = -1;
189
+ }
190
+ }
191
+ if (d === 1)
192
+ console.log(chalk.greenBright("Database decrypted successfully"));
193
+ if (d > -1) {
194
+ const backupDataRestore = JSON.parse(
195
+ fs.readFileSync(backupFile, "utf8"),
196
+ );
197
+
198
+ fs.mkdirSync(db.storage.baseDir, { recursive: true });
199
+ fs.writeFileSync(
200
+ path.join(db.storage.baseDir, "snowbase.json"),
201
+ JSON.stringify(backupDataRestore),
202
+ );
203
+ db.storage._cache = backupDataRestore;
204
+ db.storage._cacheMtime = fs.statSync(backupFile).mtimeMs;
205
+ db.storage._cacheSize = fs.statSync(backupFile).size;
206
+ console.log(`Database restored from ${backupFile}`);
207
+ } else
208
+ console.log(
209
+ chalk.red("Decryption failed, please provide a valid secret"),
210
+ 'Example: snowbase restore --secret <secret> --restoreFile <backup-ID>',
211
+ );
212
+ } else {
213
+ console.log(chalk.red(`Backup file ${backupFile} does not exist. Check the backup ID or if the dir exists`));
214
+ }
215
+ break;
216
+ }
217
+
218
+ case "clear": {
219
+ let all = db.get();
220
+ let keys = Object.keys(all || {});
221
+ let d = 0;
222
+ if (
223
+ keys.length === 4 &&
224
+ keys.sort().every((v, i) => v === ["data", "iv", "salt", "tag"][i])
225
+ ) {
226
+ d = 1;
227
+ console.log(chalk.blue("Database is encrypted, wait..."));
228
+ try {
229
+ all = db._decrypt(JSON.stringify(all), config.secret);
230
+ } catch (error) {
231
+ d = -1;
232
+ }
233
+ }
234
+ if (d === 1)
235
+ console.log(chalk.greenBright("Database decrypted successfully"));
236
+ if (d > -1) {
237
+ db.clear();
238
+ console.log("Database cleared.");
239
+ } else
240
+ console.log(
241
+ chalk.red("Decryption failed, please provide a valid secret"),
242
+ );
243
+ break;
244
+ }
245
+
246
+ case "get": {
247
+ let all = db.get();
248
+ let keys = Object.keys(all || {});
249
+ let d = 0;
250
+ if (
251
+ keys.length === 4 &&
252
+ keys.sort().every((v, i) => v === ["data", "iv", "salt", "tag"][i])
253
+ ) {
254
+ d = 1;
255
+ console.log(chalk.blue("Database is encrypted, wait..."));
256
+ try {
257
+ all = db._decrypt(JSON.stringify(all), config.secret);
258
+ } catch (error) {
259
+ d = -1;
260
+ }
261
+ }
262
+ if (d === 1)
263
+ console.log(chalk.greenBright("Database decrypted successfully"));
264
+ if (d > -1) {
265
+ if (!config.key) console.log(all);
266
+ else console.log(db._getTargetFromData(all, config.key));
267
+ } else
268
+ console.log(
269
+ chalk.red("Decryption failed, please provide a valid secret"),
270
+ );
271
+ break;
272
+ }
273
+
274
+ case "set": {
275
+ if (!config.key) console.log(db.get());
276
+ else console.log(db.get(config.key));
277
+ break;
278
+ }
279
+ case "remove": {
280
+ let run = db.remove(config.key);
281
+ if (run) console.log(chalk.green("Value removed successfully"));
282
+ else console.log(chalk.red("Value not removed"));
283
+ break;
284
+ }
285
+ case "has": {
286
+ let run = db.has(config.key);
287
+ console.log(run);
288
+ break;
289
+ }
290
+ }
291
+ }
package/package.json CHANGED
@@ -1,24 +1,58 @@
1
1
  {
2
2
  "name": "snowbase",
3
- "version": "5.0.0",
4
- "description": "Local and Global Server database using json",
5
- "main": "index.js",
6
- "dependencies": {
7
- "node-fetch": "^2.6.1"
8
- },
9
- "devDependencies": {},
10
- "scripts": {
11
- "test": "echo \"Error: no test specified\" && exit 1"
12
- },
13
- "keywords": [],
14
- "author": "mr.frozenfire",
15
- "license": "ISC",
3
+ "version": "6.0.0",
4
+ "description": "A lightweight local JSON database for modern JavaScript projects.",
5
+ "keywords": [
6
+ "file-database",
7
+ "local storage",
8
+ "database",
9
+ "json",
10
+ "crypto",
11
+ "lightweight",
12
+ "db"
13
+ ],
14
+ "homepage": "https://github.com/p3droob/snowbase#readme",
16
15
  "repository": {
17
16
  "type": "git",
18
- "url": "git+https://github.com/FrozenFireBR/snowbase.git"
17
+ "url": "https://github.com/p3droob/snowbase"
19
18
  },
20
19
  "bugs": {
21
- "url": "https://github.com/FrozenFireBR/snowbase/issues"
20
+ "url": "https://github.com/p3droob/snowbase/issues"
21
+ },
22
+ "license": "MIT",
23
+ "author": "p3droob",
24
+ "main": "./src/Snowbase.js",
25
+ "type": "module",
26
+ "exports": {
27
+ ".": "./src/Snowbase.js",
28
+ "./async": "./src/SnowbaseAsync.js"
29
+ },
30
+ "bin": {
31
+ "snowbase": "./bin/snowbase.js"
22
32
  },
23
- "homepage": "https://github.com/FrozenFireBR/snowbase#readme"
33
+ "files": [
34
+ "src",
35
+ "bin",
36
+ "README.md",
37
+ "LICENSE",
38
+ "CHANGELOG.md"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsc",
42
+ "dev": "tsc --watch",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "node test/index.js",
45
+ "test:comprehensive": "node test/comprehensive.js",
46
+ "test:async": "node test/indexAsync.js"
47
+ },
48
+ "engines": {
49
+ "node": ">=16"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^26.0.1",
53
+ "typescript": "^6.0.3"
54
+ },
55
+ "dependencies": {
56
+ "chalk": "^5.6.2"
57
+ }
24
58
  }