dimond-db 1.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/LICENSE +21 -0
- package/README.md +362 -0
- package/package.json +46 -0
- package/src/database/Collection.js +401 -0
- package/src/database/Database.js +121 -0
- package/src/engine/QueryEngine.js +166 -0
- package/src/engine/StorageEngine.js +177 -0
- package/src/errors/DatabaseError.js +68 -0
- package/src/index.js +18 -0
- package/src/query/operators.js +187 -0
- package/src/storage/FileStorage.js +131 -0
- package/src/utils/deepClone.js +31 -0
- package/src/utils/idGenerator.js +35 -0
- package/src/utils/validator.js +133 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LocalDB Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# LocalDB JS
|
|
2
|
+
|
|
3
|
+
A lightweight embedded database engine for Node.js with a MongoDB-like API. No external database server required.
|
|
4
|
+
|
|
5
|
+
## π Features
|
|
6
|
+
|
|
7
|
+
- **Zero Configuration** - Works out of the box with no setup required
|
|
8
|
+
- **Offline First** - All data stored locally in your project
|
|
9
|
+
- **MongoDB-like API** - Familiar syntax for MongoDB users
|
|
10
|
+
- **Automatic Persistence** - Data automatically saved to disk
|
|
11
|
+
- **Query Operators** - Support for `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$exists`, `$and`, `$or`
|
|
12
|
+
- **Update Operators** - Support for `$set`, `$unset`, `$inc`, `$push`
|
|
13
|
+
- **Type Safety** - Comprehensive validation and error handling
|
|
14
|
+
- **Lightweight** - No external dependencies
|
|
15
|
+
- **ES Modules** - Modern JavaScript with async/await
|
|
16
|
+
|
|
17
|
+
## π¦ Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install dimond-db
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## π― Quick Start
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
import { LocalDB } from 'dimond-db';
|
|
27
|
+
|
|
28
|
+
// Create database instance
|
|
29
|
+
const db = new LocalDB({
|
|
30
|
+
database: 'myDatabase'
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Get a collection
|
|
34
|
+
const users = db.collection('users');
|
|
35
|
+
|
|
36
|
+
// Insert a document
|
|
37
|
+
await users.insertOne({
|
|
38
|
+
name: 'Arif',
|
|
39
|
+
age: 22,
|
|
40
|
+
email: 'arif@example.com'
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Find documents
|
|
44
|
+
const allUsers = await users.find();
|
|
45
|
+
console.log(allUsers);
|
|
46
|
+
|
|
47
|
+
// Find with query
|
|
48
|
+
const adults = await users.find({ age: { $gte: 18 } });
|
|
49
|
+
console.log(adults);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## π API Reference
|
|
53
|
+
|
|
54
|
+
### Database
|
|
55
|
+
|
|
56
|
+
#### Constructor
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const db = new LocalDB({
|
|
60
|
+
database: 'databaseName', // required
|
|
61
|
+
path: './database' // optional, defaults to './database'
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
#### Methods
|
|
66
|
+
|
|
67
|
+
- `db.collection(name)` - Get or create a collection
|
|
68
|
+
- `db.listCollections()` - List all collections
|
|
69
|
+
- `db.dropDatabase()` - Drop the entire database
|
|
70
|
+
- `db.close()` - Close database connection
|
|
71
|
+
- `db.stats()` - Get database statistics
|
|
72
|
+
|
|
73
|
+
### Collection
|
|
74
|
+
|
|
75
|
+
#### Insert Operations
|
|
76
|
+
|
|
77
|
+
**insertOne(document)**
|
|
78
|
+
```javascript
|
|
79
|
+
const result = await users.insertOne({
|
|
80
|
+
name: 'Ali',
|
|
81
|
+
age: 25
|
|
82
|
+
});
|
|
83
|
+
// Returns: { acknowledged: true, insertedId: 'generated-uuid' }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**insertMany(documents)**
|
|
87
|
+
```javascript
|
|
88
|
+
const result = await users.insertMany([
|
|
89
|
+
{ name: 'Sara', age: 28 },
|
|
90
|
+
{ name: 'Ahmed', age: 30 }
|
|
91
|
+
]);
|
|
92
|
+
// Returns: { acknowledged: true, insertedCount: 2, insertedIds: [...] }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Find Operations
|
|
96
|
+
|
|
97
|
+
**find(filter)**
|
|
98
|
+
```javascript
|
|
99
|
+
// Find all
|
|
100
|
+
const all = await users.find();
|
|
101
|
+
|
|
102
|
+
// Find with filter
|
|
103
|
+
const adults = await users.find({ age: { $gte: 18 } });
|
|
104
|
+
|
|
105
|
+
// Find with multiple conditions
|
|
106
|
+
const result = await users.find({
|
|
107
|
+
age: { $gte: 18, $lt: 65 },
|
|
108
|
+
status: 'active'
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**findOne(filter)**
|
|
113
|
+
```javascript
|
|
114
|
+
const user = await users.findOne({ name: 'Arif' });
|
|
115
|
+
// Returns: document or null
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### Update Operations
|
|
119
|
+
|
|
120
|
+
**updateOne(filter, update)**
|
|
121
|
+
```javascript
|
|
122
|
+
await users.updateOne(
|
|
123
|
+
{ name: 'Arif' },
|
|
124
|
+
{ $set: { age: 23, city: 'Karachi' } }
|
|
125
|
+
);
|
|
126
|
+
// Returns: { acknowledged: true, matchedCount: 1, modifiedCount: 1 }
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**updateMany(filter, update)**
|
|
130
|
+
```javascript
|
|
131
|
+
await users.updateMany(
|
|
132
|
+
{ status: 'inactive' },
|
|
133
|
+
{ $set: { status: 'active' } }
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### Delete Operations
|
|
138
|
+
|
|
139
|
+
**deleteOne(filter)**
|
|
140
|
+
```javascript
|
|
141
|
+
await users.deleteOne({ name: 'Arif' });
|
|
142
|
+
// Returns: { acknowledged: true, deletedCount: 1 }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**deleteMany(filter)**
|
|
146
|
+
```javascript
|
|
147
|
+
await users.deleteMany({ age: { $lt: 18 } });
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Other Operations
|
|
151
|
+
|
|
152
|
+
**countDocuments(filter)**
|
|
153
|
+
```javascript
|
|
154
|
+
const count = await users.countDocuments({ age: { $gte: 18 } });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**drop()**
|
|
158
|
+
```javascript
|
|
159
|
+
await users.drop();
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## π Query Operators
|
|
163
|
+
|
|
164
|
+
### Comparison Operators
|
|
165
|
+
|
|
166
|
+
| Operator | Description | Example |
|
|
167
|
+
|----------|-------------|---------|
|
|
168
|
+
| `$eq` | Equal to | `{ age: { $eq: 25 } }` |
|
|
169
|
+
| `$ne` | Not equal to | `{ status: { $ne: 'inactive' } }` |
|
|
170
|
+
| `$gt` | Greater than | `{ age: { $gt: 18 } }` |
|
|
171
|
+
| `$gte` | Greater than or equal | `{ age: { $gte: 18 } }` |
|
|
172
|
+
| `$lt` | Less than | `{ age: { $lt: 65 } }` |
|
|
173
|
+
| `$lte` | Less than or equal | `{ age: { $lte: 65 } }` |
|
|
174
|
+
| `$in` | In array | `{ status: { $in: ['active', 'pending'] } }` |
|
|
175
|
+
| `$nin` | Not in array | `{ status: { $nin: ['banned', 'deleted'] } }` |
|
|
176
|
+
| `$exists` | Field exists | `{ email: { $exists: true } }` |
|
|
177
|
+
|
|
178
|
+
### Logical Operators
|
|
179
|
+
|
|
180
|
+
**$and**
|
|
181
|
+
```javascript
|
|
182
|
+
await users.find({
|
|
183
|
+
$and: [
|
|
184
|
+
{ age: { $gte: 18 } },
|
|
185
|
+
{ age: { $lte: 65 } }
|
|
186
|
+
]
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**$or**
|
|
191
|
+
```javascript
|
|
192
|
+
await users.find({
|
|
193
|
+
$or: [
|
|
194
|
+
{ status: 'premium' },
|
|
195
|
+
{ age: { $gte: 65 } }
|
|
196
|
+
]
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## π§ Update Operators
|
|
201
|
+
|
|
202
|
+
### $set
|
|
203
|
+
Set field values
|
|
204
|
+
```javascript
|
|
205
|
+
await users.updateOne(
|
|
206
|
+
{ name: 'Arif' },
|
|
207
|
+
{ $set: { age: 23, city: 'Karachi' } }
|
|
208
|
+
);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### $unset
|
|
212
|
+
Remove fields
|
|
213
|
+
```javascript
|
|
214
|
+
await users.updateOne(
|
|
215
|
+
{ name: 'Arif' },
|
|
216
|
+
{ $unset: { tempField: '' } }
|
|
217
|
+
);
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### $inc
|
|
221
|
+
Increment numeric values
|
|
222
|
+
```javascript
|
|
223
|
+
await users.updateOne(
|
|
224
|
+
{ name: 'Arif' },
|
|
225
|
+
{ $inc: { loginCount: 1 } }
|
|
226
|
+
);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### $push
|
|
230
|
+
Add to array
|
|
231
|
+
```javascript
|
|
232
|
+
await users.updateOne(
|
|
233
|
+
{ name: 'Arif' },
|
|
234
|
+
{ $push: { tags: 'developer' } }
|
|
235
|
+
);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## π Storage Structure
|
|
239
|
+
|
|
240
|
+
LocalDB automatically creates this structure:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
your-project/
|
|
244
|
+
βββ database/
|
|
245
|
+
βββ myDatabase/
|
|
246
|
+
βββ metadata.json
|
|
247
|
+
βββ collections/
|
|
248
|
+
βββ users.collection
|
|
249
|
+
βββ products.collection
|
|
250
|
+
βββ orders.collection
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Each collection is stored as a JSON file with automatic persistence.
|
|
254
|
+
|
|
255
|
+
## π¨ Complete Example
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
import { LocalDB } from 'dimond-db';
|
|
259
|
+
|
|
260
|
+
const db = new LocalDB({ database: 'shop' });
|
|
261
|
+
const products = db.collection('products');
|
|
262
|
+
|
|
263
|
+
// Insert products
|
|
264
|
+
await products.insertMany([
|
|
265
|
+
{ name: 'Laptop', price: 999, category: 'electronics', stock: 50 },
|
|
266
|
+
{ name: 'Mouse', price: 25, category: 'electronics', stock: 200 },
|
|
267
|
+
{ name: 'Desk', price: 300, category: 'furniture', stock: 30 }
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
// Find expensive products
|
|
271
|
+
const expensive = await products.find({
|
|
272
|
+
price: { $gte: 500 }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Update stock
|
|
276
|
+
await products.updateOne(
|
|
277
|
+
{ name: 'Laptop' },
|
|
278
|
+
{ $inc: { stock: -1 } }
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Find by category
|
|
282
|
+
const electronics = await products.find({
|
|
283
|
+
category: 'electronics',
|
|
284
|
+
stock: { $gt: 0 }
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Count products
|
|
288
|
+
const totalProducts = await products.countDocuments();
|
|
289
|
+
|
|
290
|
+
// Get database stats
|
|
291
|
+
const stats = await db.stats();
|
|
292
|
+
console.log(stats);
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## π‘οΈ Error Handling
|
|
296
|
+
|
|
297
|
+
LocalDB provides specific error types for better error handling:
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
import {
|
|
301
|
+
LocalDB,
|
|
302
|
+
DuplicateKeyError,
|
|
303
|
+
ValidationError,
|
|
304
|
+
QueryError,
|
|
305
|
+
StorageError
|
|
306
|
+
} from 'dimond-db';
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
await users.insertOne({ _id: 'duplicate-id' });
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (error instanceof DuplicateKeyError) {
|
|
312
|
+
console.log('Document with this ID already exists');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## β‘ Performance Tips
|
|
318
|
+
|
|
319
|
+
1. **Batch Inserts** - Use `insertMany()` instead of multiple `insertOne()` calls
|
|
320
|
+
2. **Selective Queries** - Use specific filters to reduce memory usage
|
|
321
|
+
3. **Regular Cleanup** - Remove old/unused documents periodically
|
|
322
|
+
4. **Collection Size** - Keep collections reasonably sized (< 10,000 documents recommended)
|
|
323
|
+
|
|
324
|
+
## π Data Validation
|
|
325
|
+
|
|
326
|
+
LocalDB validates:
|
|
327
|
+
- Document structure (must be objects, not arrays or primitives)
|
|
328
|
+
- Duplicate `_id` values
|
|
329
|
+
- Query syntax
|
|
330
|
+
- Update operators
|
|
331
|
+
- Collection names
|
|
332
|
+
|
|
333
|
+
## π Requirements
|
|
334
|
+
|
|
335
|
+
- Node.js >= 14.0.0
|
|
336
|
+
- ES Modules support
|
|
337
|
+
|
|
338
|
+
## πΊοΈ Roadmap
|
|
339
|
+
|
|
340
|
+
Future versions may include:
|
|
341
|
+
- Indexing for faster queries
|
|
342
|
+
- Transactions
|
|
343
|
+
- Aggregation pipeline
|
|
344
|
+
- Schema validation
|
|
345
|
+
- Backup/restore utilities
|
|
346
|
+
- Replication support
|
|
347
|
+
|
|
348
|
+
## π License
|
|
349
|
+
|
|
350
|
+
MIT License - see LICENSE file for details
|
|
351
|
+
|
|
352
|
+
## π€ Contributing
|
|
353
|
+
|
|
354
|
+
Contributions are welcome! Please feel free to submit issues or pull requests.
|
|
355
|
+
|
|
356
|
+
## π§ Support
|
|
357
|
+
|
|
358
|
+
For issues and questions, please open an issue on GitHub.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
**LocalDB JS** - Built with β€οΈ for developers who need a simple, embedded database solution.
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dimond-db",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight embedded database engine for Node.js with MongoDB-like API. No external database server required.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=14.0.0"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test tests/",
|
|
12
|
+
"test:watch": "node --test --watch tests/",
|
|
13
|
+
"demo": "node demo.js",
|
|
14
|
+
"prepublishOnly": "npm test"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"database",
|
|
18
|
+
"embedded",
|
|
19
|
+
"local",
|
|
20
|
+
"mongodb",
|
|
21
|
+
"nosql",
|
|
22
|
+
"storage",
|
|
23
|
+
"lightweight",
|
|
24
|
+
"offline",
|
|
25
|
+
"filesystem",
|
|
26
|
+
"json",
|
|
27
|
+
"query",
|
|
28
|
+
"document-database",
|
|
29
|
+
"node-database"
|
|
30
|
+
],
|
|
31
|
+
"files": [
|
|
32
|
+
"src/",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"author": "LocalDB Contributors",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/imarifluqman/dimond-db"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/imarifluqman/dimond-db/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/imarifluqman/dimond-db#readme"
|
|
46
|
+
}
|