nodejs-json-db 0.0.1 โ 0.0.2
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/README.md +142 -53
- package/dist/index.d.ts +296 -10
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +97 -88
package/README.md
CHANGED
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
|
|
7
|
-
A production-ready, lightweight JSON-based database for Node.js and Electron applications. Zero external database dependencies, fully typed, with a MongoDB-like query API
|
|
7
|
+
A production-ready, lightweight JSON-based database for Node.js and Electron applications. Zero external database dependencies, fully typed, with a MongoDB-like query API. Supports three storage modes: Standard, High-Concurrency, and Lazy Loading.
|
|
8
8
|
|
|
9
9
|
## โจ Features
|
|
10
10
|
|
|
11
11
|
- ๐ **Zero Dependencies** - No external database server required
|
|
12
12
|
- ๐ฆ **Dual Module Support** - Works with both ESM and CommonJS
|
|
13
13
|
- ๐ **Type-Safe** - Full TypeScript support with generics
|
|
14
|
-
- ๐ **MongoDB-like Queries** -
|
|
14
|
+
- ๐ **MongoDB-like Queries** - Rich query operators (`$eq`, `$gt`, `$in`, `$regex`, `$all`, `$elemMatch`, etc.)
|
|
15
|
+
- ๐ฏ **Field Projection** - Select which fields to return in queries
|
|
15
16
|
- โก **High Performance** - Memory caching with atomic file writes
|
|
16
17
|
- ๐ **High-Concurrency Mode** - Partitioned storage with write batching for massive throughput
|
|
18
|
+
- ๐พ **Lazy Loading Mode** - Memory-efficient storage for huge datasets with LRU caching
|
|
17
19
|
- โ
**Schema Validation** - Optional Zod integration for document validation
|
|
18
20
|
- ๐ฅ๏ธ **Electron Ready** - Perfect for desktop applications
|
|
19
|
-
- ๐งช **Well Tested** -
|
|
21
|
+
- ๐งช **Well Tested** - 141 tests with comprehensive coverage
|
|
20
22
|
|
|
21
23
|
## ๐ฆ Installation
|
|
22
24
|
|
|
@@ -29,6 +31,7 @@ yarn add nodejs-json-db
|
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
For schema validation (optional):
|
|
34
|
+
|
|
32
35
|
```bash
|
|
33
36
|
npm install zod
|
|
34
37
|
```
|
|
@@ -75,21 +78,22 @@ Performance tested with documents up to 1 million records:
|
|
|
75
78
|
|
|
76
79
|
### Write Performance (insertMany)
|
|
77
80
|
|
|
78
|
-
| Documents | Standard Mode
|
|
79
|
-
|
|
80
|
-
| 100,000
|
|
81
|
-
| 500,000
|
|
82
|
-
| 1,000,000 | 392K docs/sec (56 MB/s) | **392K docs/sec (56 MB/s)**
|
|
81
|
+
| Documents | Standard Mode | High-Concurrency Mode |
|
|
82
|
+
| --------- | ----------------------- | ------------------------------ |
|
|
83
|
+
| 100,000 | 390K docs/sec (56 MB/s) | 355K docs/sec (51 MB/s) |
|
|
84
|
+
| 500,000 | 382K docs/sec (55 MB/s) | **423K docs/sec (61 MB/s)** โ
|
|
|
85
|
+
| 1,000,000 | 392K docs/sec (56 MB/s) | **392K docs/sec (56 MB/s)** |
|
|
83
86
|
|
|
84
87
|
### Read Performance (find)
|
|
85
88
|
|
|
86
|
-
| Documents | Standard Mode
|
|
87
|
-
|
|
88
|
-
| 1,000
|
|
89
|
-
| 10,000
|
|
90
|
-
| 100,000+
|
|
89
|
+
| Documents | Standard Mode | High-Concurrency Mode |
|
|
90
|
+
| --------- | ------------------------ | ------------------------------- |
|
|
91
|
+
| 1,000 | 648K docs/sec (93 MB/s) | **803K docs/sec (115 MB/s)** โ
|
|
|
92
|
+
| 10,000 | 1.5M docs/sec (218 MB/s) | **2.2M docs/sec (309 MB/s)** โ
|
|
|
93
|
+
| 100,000+ | **2.8M+ docs/sec** | 2.0M docs/sec |
|
|
91
94
|
|
|
92
95
|
Run benchmarks yourself:
|
|
96
|
+
|
|
93
97
|
```bash
|
|
94
98
|
npx tsx examples/benchmark.ts
|
|
95
99
|
```
|
|
@@ -103,10 +107,10 @@ const db = new JsonDB({
|
|
|
103
107
|
dataDir: './data',
|
|
104
108
|
highConcurrency: {
|
|
105
109
|
enabled: true,
|
|
106
|
-
partitions: 16,
|
|
107
|
-
batchSize: 1000,
|
|
108
|
-
flushInterval: 100,
|
|
109
|
-
maxConcurrentIO: 4,
|
|
110
|
+
partitions: 16, // Data shards (default: 16)
|
|
111
|
+
batchSize: 1000, // Writes before auto-flush (default: 1000)
|
|
112
|
+
flushInterval: 100, // Max ms before flush (default: 100)
|
|
113
|
+
maxConcurrentIO: 4, // Parallel I/O operations (default: 4)
|
|
110
114
|
},
|
|
111
115
|
});
|
|
112
116
|
|
|
@@ -127,12 +131,57 @@ await db.close();
|
|
|
127
131
|
|
|
128
132
|
### When to Use High-Concurrency Mode
|
|
129
133
|
|
|
130
|
-
| Use Case
|
|
131
|
-
|
|
132
|
-
| Desktop apps, small datasets
|
|
133
|
-
| Web servers with concurrent requests | High-Concurrency
|
|
134
|
-
| Bulk data import
|
|
135
|
-
| Real-time applications
|
|
134
|
+
| Use Case | Recommended Mode |
|
|
135
|
+
| ------------------------------------ | ------------------ |
|
|
136
|
+
| Desktop apps, small datasets | Standard |
|
|
137
|
+
| Web servers with concurrent requests | High-Concurrency |
|
|
138
|
+
| Bulk data import | Either (both fast) |
|
|
139
|
+
| Real-time applications | High-Concurrency |
|
|
140
|
+
|
|
141
|
+
## ๐พ Lazy Loading Mode
|
|
142
|
+
|
|
143
|
+
For applications with huge datasets that don't fit in memory, enable lazy loading mode:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const db = new JsonDB({
|
|
147
|
+
dataDir: './data',
|
|
148
|
+
lazyLoading: {
|
|
149
|
+
enabled: true,
|
|
150
|
+
cacheSize: 1000, // Max documents in memory (default: 1000)
|
|
151
|
+
chunkSize: 10000, // Future: documents per chunk file
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await db.connect();
|
|
156
|
+
const users = db.collection('users');
|
|
157
|
+
|
|
158
|
+
// Documents are loaded on-demand with LRU caching
|
|
159
|
+
const user = await users.findById('some-id'); // Efficient - uses cache
|
|
160
|
+
const count = await users.count(); // Efficient - uses index only
|
|
161
|
+
|
|
162
|
+
// Queries still work but load documents from disk
|
|
163
|
+
const results = await users.find({ age: { $gt: 25 } });
|
|
164
|
+
|
|
165
|
+
await db.close();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### How It Works
|
|
169
|
+
|
|
170
|
+
- **Index in memory**: Only document IDs are kept in memory
|
|
171
|
+
- **LRU cache**: Frequently accessed documents are cached (up to `cacheSize`)
|
|
172
|
+
- **On-demand loading**: Full documents are loaded from disk when needed
|
|
173
|
+
- **ID-based optimization**: `findById()` and `count()` are optimized
|
|
174
|
+
|
|
175
|
+
### When to Use Lazy Loading Mode
|
|
176
|
+
|
|
177
|
+
| Use Case | Recommended Mode |
|
|
178
|
+
| ------------------------------ | ---------------- |
|
|
179
|
+
| Small datasets (< 10K docs) | Standard |
|
|
180
|
+
| Medium datasets | Standard or Lazy |
|
|
181
|
+
| Huge datasets (> 100K docs) | Lazy Loading |
|
|
182
|
+
| Memory-constrained environment | Lazy Loading |
|
|
183
|
+
| Frequent full queries | Standard |
|
|
184
|
+
| ID-based access patterns | Lazy Loading |
|
|
136
185
|
|
|
137
186
|
## โ
Schema Validation (Zod)
|
|
138
187
|
|
|
@@ -174,14 +223,15 @@ try {
|
|
|
174
223
|
|
|
175
224
|
### JsonDB Options
|
|
176
225
|
|
|
177
|
-
| Option
|
|
178
|
-
|
|
179
|
-
| `dataDir`
|
|
180
|
-
| `autoSave`
|
|
181
|
-
| `saveDebounce`
|
|
182
|
-
| `prettyPrint`
|
|
183
|
-
| `fileExtension`
|
|
184
|
-
| `highConcurrency` | `object`
|
|
226
|
+
| Option | Type | Default | Description |
|
|
227
|
+
| ----------------- | --------- | ------------ | ------------------------------------------ |
|
|
228
|
+
| `dataDir` | `string` | **required** | Path to store JSON files |
|
|
229
|
+
| `autoSave` | `boolean` | `true` | Auto-save after writes |
|
|
230
|
+
| `saveDebounce` | `number` | `0` | Debounce time (ms) for saves |
|
|
231
|
+
| `prettyPrint` | `boolean` | `true` | Format JSON files |
|
|
232
|
+
| `fileExtension` | `string` | `.json` | Custom file extension |
|
|
233
|
+
| `highConcurrency` | `object` | `undefined` | Enable high-concurrency mode |
|
|
234
|
+
| `lazyLoading` | `object` | `undefined` | Enable lazy loading mode for huge datasets |
|
|
185
235
|
|
|
186
236
|
### JsonDB Methods
|
|
187
237
|
|
|
@@ -234,21 +284,38 @@ collection.getName(); // Get collection name
|
|
|
234
284
|
|
|
235
285
|
### Comparison
|
|
236
286
|
|
|
237
|
-
| Operator
|
|
238
|
-
|
|
239
|
-
| `$eq`
|
|
240
|
-
| `$ne`
|
|
241
|
-
| `$gt` / `$gte` | `{ age: { $gte: 18 } }`
|
|
242
|
-
| `$lt` / `$lte` | `{ price: { $lt: 100 } }`
|
|
287
|
+
| Operator | Example |
|
|
288
|
+
| -------------- | ------------------------------------- |
|
|
289
|
+
| `$eq` | `{ age: { $eq: 25 } }` |
|
|
290
|
+
| `$ne` | `{ status: { $ne: 'deleted' } }` |
|
|
291
|
+
| `$gt` / `$gte` | `{ age: { $gte: 18 } }` |
|
|
292
|
+
| `$lt` / `$lte` | `{ price: { $lt: 100 } }` |
|
|
243
293
|
| `$in` / `$nin` | `{ role: { $in: ['admin', 'mod'] } }` |
|
|
244
294
|
|
|
245
295
|
### String
|
|
246
296
|
|
|
247
|
-
| Operator
|
|
248
|
-
|
|
249
|
-
| `$regex`
|
|
250
|
-
| `$startsWith` | `{ name: { $startsWith: 'John' } }`
|
|
251
|
-
| `$endsWith`
|
|
297
|
+
| Operator | Example |
|
|
298
|
+
| ------------- | --------------------------------------- |
|
|
299
|
+
| `$regex` | `{ email: { $regex: /@gmail\.com$/ } }` |
|
|
300
|
+
| `$startsWith` | `{ name: { $startsWith: 'John' } }` |
|
|
301
|
+
| `$endsWith` | `{ email: { $endsWith: '.com' } }` |
|
|
302
|
+
|
|
303
|
+
### Array
|
|
304
|
+
|
|
305
|
+
| Operator | Description | Example |
|
|
306
|
+
| ------------ | ------------------------- | ------------------------------------------------ |
|
|
307
|
+
| `$contains` | Array contains value | `{ tags: { $contains: 'admin' } }` |
|
|
308
|
+
| `$all` | Array contains all values | `{ tags: { $all: ['user', 'premium'] } }` |
|
|
309
|
+
| `$elemMatch` | Element matches sub-query | `{ items: { $elemMatch: { qty: { $gt: 5 } } } }` |
|
|
310
|
+
| `$size` | Array has exact length | `{ tags: { $size: 3 } }` |
|
|
311
|
+
|
|
312
|
+
### Utility
|
|
313
|
+
|
|
314
|
+
| Operator | Description | Example |
|
|
315
|
+
| --------- | ---------------- | ------------------------------ |
|
|
316
|
+
| `$type` | Value type check | `{ age: { $type: 'number' } }` |
|
|
317
|
+
| `$mod` | Modulo operation | `{ qty: { $mod: [4, 0] } }` |
|
|
318
|
+
| `$exists` | Field exists | `{ email: { $exists: true } }` |
|
|
252
319
|
|
|
253
320
|
### Logical
|
|
254
321
|
|
|
@@ -263,23 +330,33 @@ collection.getName(); // Get collection name
|
|
|
263
330
|
{ $not: { status: 'deleted' } }
|
|
264
331
|
```
|
|
265
332
|
|
|
266
|
-
|
|
333
|
+
## ๐ฏ Projection
|
|
334
|
+
|
|
335
|
+
Select which fields to return in query results:
|
|
267
336
|
|
|
268
337
|
```typescript
|
|
269
|
-
|
|
270
|
-
{
|
|
338
|
+
// Include only specific fields (_id included by default)
|
|
339
|
+
await users.find({}, { projection: { name: 1, email: 1 } });
|
|
340
|
+
|
|
341
|
+
// Exclude specific fields
|
|
342
|
+
await users.find({}, { projection: { password: 0, secret: 0 } });
|
|
343
|
+
|
|
344
|
+
// Exclude _id
|
|
345
|
+
await users.find({}, { projection: { name: 1, _id: 0 } });
|
|
271
346
|
```
|
|
272
347
|
|
|
348
|
+
> **Note:** Cannot mix inclusion and exclusion (except for `_id`).
|
|
349
|
+
|
|
273
350
|
## ๐ Update Operators
|
|
274
351
|
|
|
275
|
-
| Operator
|
|
276
|
-
|
|
277
|
-
| `$set`
|
|
278
|
-
| `$unset`
|
|
279
|
-
| `$inc`
|
|
280
|
-
| `$push`
|
|
281
|
-
| `$pull`
|
|
282
|
-
| `$addToSet` | Add unique
|
|
352
|
+
| Operator | Description | Example |
|
|
353
|
+
| ----------- | ----------------- | ----------------------------------- |
|
|
354
|
+
| `$set` | Set field | `{ $set: { name: 'New' } }` |
|
|
355
|
+
| `$unset` | Remove field | `{ $unset: { temp: true } }` |
|
|
356
|
+
| `$inc` | Increment | `{ $inc: { views: 1 } }` |
|
|
357
|
+
| `$push` | Add to array | `{ $push: { tags: 'new' } }` |
|
|
358
|
+
| `$pull` | Remove from array | `{ $pull: { tags: 'old' } }` |
|
|
359
|
+
| `$addToSet` | Add unique | `{ $addToSet: { tags: 'unique' } }` |
|
|
283
360
|
|
|
284
361
|
## ๐ฅ๏ธ Electron Integration
|
|
285
362
|
|
|
@@ -298,6 +375,7 @@ await db.connect();
|
|
|
298
375
|
## ๐ Data Storage
|
|
299
376
|
|
|
300
377
|
Standard mode stores one JSON file per collection:
|
|
378
|
+
|
|
301
379
|
```
|
|
302
380
|
data/
|
|
303
381
|
โโโ users.json
|
|
@@ -306,6 +384,7 @@ data/
|
|
|
306
384
|
```
|
|
307
385
|
|
|
308
386
|
High-concurrency mode partitions data:
|
|
387
|
+
|
|
309
388
|
```
|
|
310
389
|
data/
|
|
311
390
|
โโโ users_p0.json
|
|
@@ -314,6 +393,16 @@ data/
|
|
|
314
393
|
โโโ ...
|
|
315
394
|
```
|
|
316
395
|
|
|
396
|
+
Lazy loading mode uses index files for fast loading:
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
data/
|
|
400
|
+
โโโ users.json # Full document data
|
|
401
|
+
โโโ users.index.json # ID index for fast access
|
|
402
|
+
โโโ posts.json
|
|
403
|
+
โโโ posts.index.json
|
|
404
|
+
```
|
|
405
|
+
|
|
317
406
|
## ๐งช Examples
|
|
318
407
|
|
|
319
408
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ interface ComparisonOperators<T> {
|
|
|
22
22
|
$startsWith?: string;
|
|
23
23
|
$endsWith?: string;
|
|
24
24
|
$contains?: T extends unknown[] ? T[number] : never;
|
|
25
|
+
/** Array must contain all specified values */
|
|
26
|
+
$all?: T extends unknown[] ? T : never;
|
|
27
|
+
/** Array element must match the sub-query */
|
|
28
|
+
$elemMatch?: T extends unknown[] ? Record<string, unknown> : never;
|
|
29
|
+
/** Array must have exact length */
|
|
30
|
+
$size?: number;
|
|
31
|
+
/** Value must be of specified type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'null' */
|
|
32
|
+
$type?: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'null' | 'undefined';
|
|
33
|
+
/** Modulo operation: [divisor, remainder] - matches if value % divisor === remainder */
|
|
34
|
+
$mod?: [number, number];
|
|
25
35
|
}
|
|
26
36
|
/**
|
|
27
37
|
* Field-level query type
|
|
@@ -68,6 +78,16 @@ type SortOrder = 1 | -1 | 'asc' | 'desc';
|
|
|
68
78
|
type Sort<T> = {
|
|
69
79
|
[K in keyof T]?: SortOrder;
|
|
70
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Projection specification for field selection
|
|
83
|
+
* Use 1 to include fields, 0 to exclude fields
|
|
84
|
+
* Cannot mix inclusion and exclusion (except _id)
|
|
85
|
+
*/
|
|
86
|
+
type Projection<T> = {
|
|
87
|
+
[K in keyof T]?: 0 | 1 | boolean;
|
|
88
|
+
} & {
|
|
89
|
+
_id?: 0 | 1 | boolean;
|
|
90
|
+
};
|
|
71
91
|
/**
|
|
72
92
|
* Options for find operations
|
|
73
93
|
*/
|
|
@@ -75,6 +95,8 @@ interface FindOptions<T> {
|
|
|
75
95
|
sort?: Sort<T>;
|
|
76
96
|
limit?: number;
|
|
77
97
|
skip?: number;
|
|
98
|
+
/** Field projection - specify which fields to include/exclude */
|
|
99
|
+
projection?: Projection<T>;
|
|
78
100
|
}
|
|
79
101
|
/**
|
|
80
102
|
* Collection configuration options
|
|
@@ -102,6 +124,17 @@ interface HighConcurrencyOptions {
|
|
|
102
124
|
/** Whether to coalesce duplicate writes (default: true) */
|
|
103
125
|
coalesceWrites?: boolean;
|
|
104
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Lazy loading mode options for memory-efficient storage
|
|
129
|
+
*/
|
|
130
|
+
interface LazyLoadingOptions {
|
|
131
|
+
/** Enable lazy loading mode */
|
|
132
|
+
enabled: boolean;
|
|
133
|
+
/** Maximum documents to keep in memory cache (default: 1000) */
|
|
134
|
+
cacheSize?: number;
|
|
135
|
+
/** Documents per chunk file (default: 10000, 0 = no chunking) */
|
|
136
|
+
chunkSize?: number;
|
|
137
|
+
}
|
|
105
138
|
/**
|
|
106
139
|
* JsonDB configuration options
|
|
107
140
|
*/
|
|
@@ -118,6 +151,8 @@ interface JsonDBOptions {
|
|
|
118
151
|
fileExtension?: string;
|
|
119
152
|
/** High-concurrency mode options (opt-in) */
|
|
120
153
|
highConcurrency?: HighConcurrencyOptions;
|
|
154
|
+
/** Lazy loading mode options for memory-efficient storage (opt-in) */
|
|
155
|
+
lazyLoading?: LazyLoadingOptions;
|
|
121
156
|
}
|
|
122
157
|
/**
|
|
123
158
|
* Internal collection data structure
|
|
@@ -266,6 +301,7 @@ declare class Collection<T extends Document> {
|
|
|
266
301
|
})[]): Promise<T[]>;
|
|
267
302
|
/**
|
|
268
303
|
* Find documents matching a query
|
|
304
|
+
* When projection is used, only specified fields are returned
|
|
269
305
|
*/
|
|
270
306
|
find(query?: Query<T>, options?: FindOptions<T>): Promise<T[]>;
|
|
271
307
|
/**
|
|
@@ -651,9 +687,252 @@ declare class HighConcurrencyCollection<T extends Document> {
|
|
|
651
687
|
}
|
|
652
688
|
|
|
653
689
|
/**
|
|
654
|
-
*
|
|
690
|
+
* LazyStorage - Memory-efficient storage adapter
|
|
691
|
+
*
|
|
692
|
+
* Instead of loading all documents into memory:
|
|
693
|
+
* - Keeps only document IDs (index) in memory
|
|
694
|
+
* - Uses LRU cache for frequently accessed documents
|
|
695
|
+
* - Loads full documents on-demand
|
|
696
|
+
* - Supports chunked storage for huge collections
|
|
697
|
+
*/
|
|
698
|
+
declare class LazyStorage {
|
|
699
|
+
private readonly dataDir;
|
|
700
|
+
private readonly fileExtension;
|
|
701
|
+
private readonly prettyPrint;
|
|
702
|
+
private readonly cacheSize;
|
|
703
|
+
private readonly chunkSize;
|
|
704
|
+
/** Document cache per collection: Map<collectionName, LRUCache<docId, document>> */
|
|
705
|
+
private readonly documentCache;
|
|
706
|
+
/** Collection metadata: Map<collectionName, metadata> */
|
|
707
|
+
private readonly collections;
|
|
708
|
+
/** Locks for concurrent access */
|
|
709
|
+
private readonly locks;
|
|
710
|
+
constructor(options: JsonDBOptions);
|
|
711
|
+
private ensureDirectory;
|
|
712
|
+
private getFilePath;
|
|
713
|
+
private getIndexFilePath;
|
|
714
|
+
private acquireLock;
|
|
715
|
+
/**
|
|
716
|
+
* Initialize a collection - loads index only, not documents
|
|
717
|
+
*/
|
|
718
|
+
initCollection(collectionName: string): Promise<void>;
|
|
719
|
+
/**
|
|
720
|
+
* Save index file for fast loading
|
|
721
|
+
*/
|
|
722
|
+
private saveIndex;
|
|
723
|
+
/**
|
|
724
|
+
* Get document count without loading documents
|
|
725
|
+
*/
|
|
726
|
+
getCount(collectionName: string): number;
|
|
727
|
+
/**
|
|
728
|
+
* Check if document exists without loading it
|
|
729
|
+
*/
|
|
730
|
+
hasDocument(collectionName: string, docId: string): boolean;
|
|
731
|
+
/**
|
|
732
|
+
* Get all document IDs without loading documents
|
|
733
|
+
*/
|
|
734
|
+
getDocumentIds(collectionName: string): string[];
|
|
735
|
+
/**
|
|
736
|
+
* Get a single document by ID - uses cache or loads from disk
|
|
737
|
+
*/
|
|
738
|
+
getDocument<T extends Document>(collectionName: string, docId: string): Promise<T | null>;
|
|
739
|
+
/**
|
|
740
|
+
* Load document from disk (internal)
|
|
741
|
+
*/
|
|
742
|
+
private loadDocumentFromDisk;
|
|
743
|
+
/**
|
|
744
|
+
* Get multiple documents by IDs (batch load)
|
|
745
|
+
*/
|
|
746
|
+
getDocuments<T extends Document>(collectionName: string, docIds: string[]): Promise<T[]>;
|
|
747
|
+
/**
|
|
748
|
+
* Load multiple documents from disk
|
|
749
|
+
*/
|
|
750
|
+
private loadDocumentsFromDisk;
|
|
751
|
+
/**
|
|
752
|
+
* Get all documents (full load - use sparingly for large collections)
|
|
753
|
+
*/
|
|
754
|
+
getAllDocuments<T extends Document>(collectionName: string): Promise<T[]>;
|
|
755
|
+
/**
|
|
756
|
+
* Insert a document
|
|
757
|
+
*/
|
|
758
|
+
insertDocument<T extends Document>(collectionName: string, doc: T): Promise<void>;
|
|
759
|
+
/**
|
|
760
|
+
* Insert multiple documents
|
|
761
|
+
*/
|
|
762
|
+
insertDocuments<T extends Document>(collectionName: string, docs: T[]): Promise<void>;
|
|
763
|
+
/**
|
|
764
|
+
* Update a document
|
|
765
|
+
*/
|
|
766
|
+
updateDocument<T extends Document>(collectionName: string, doc: T): Promise<void>;
|
|
767
|
+
/**
|
|
768
|
+
* Delete a document
|
|
769
|
+
*/
|
|
770
|
+
deleteDocument(collectionName: string, docId: string): Promise<void>;
|
|
771
|
+
/**
|
|
772
|
+
* Delete multiple documents
|
|
773
|
+
*/
|
|
774
|
+
deleteDocuments(collectionName: string, docIds: string[]): Promise<void>;
|
|
775
|
+
/**
|
|
776
|
+
* Clear all documents from a collection
|
|
777
|
+
*/
|
|
778
|
+
clearCollection(collectionName: string): Promise<void>;
|
|
779
|
+
/**
|
|
780
|
+
* Persist collection to disk
|
|
781
|
+
*/
|
|
782
|
+
private persistCollection;
|
|
783
|
+
/**
|
|
784
|
+
* Build document array from cache and disk
|
|
785
|
+
*/
|
|
786
|
+
private buildDocumentArray;
|
|
787
|
+
/**
|
|
788
|
+
* Delete a collection
|
|
789
|
+
*/
|
|
790
|
+
deleteCollection(collectionName: string): Promise<void>;
|
|
791
|
+
/**
|
|
792
|
+
* Check if collection exists
|
|
793
|
+
*/
|
|
794
|
+
exists(collectionName: string): Promise<boolean>;
|
|
795
|
+
/**
|
|
796
|
+
* List all collections
|
|
797
|
+
*/
|
|
798
|
+
list(): Promise<string[]>;
|
|
799
|
+
/**
|
|
800
|
+
* Get cache statistics
|
|
801
|
+
*/
|
|
802
|
+
getStats(): {
|
|
803
|
+
collections: number;
|
|
804
|
+
cachedDocuments: number;
|
|
805
|
+
cacheSize: number;
|
|
806
|
+
};
|
|
807
|
+
/**
|
|
808
|
+
* Clear cache for a collection
|
|
809
|
+
*/
|
|
810
|
+
clearCache(collectionName?: string): void;
|
|
811
|
+
/**
|
|
812
|
+
* Flush pending writes
|
|
813
|
+
*/
|
|
814
|
+
flush(): Promise<void>;
|
|
815
|
+
/**
|
|
816
|
+
* Get data directory path
|
|
817
|
+
*/
|
|
818
|
+
getDataDir(): string;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* LazyCollection - Memory-efficient collection using lazy loading storage
|
|
823
|
+
*
|
|
824
|
+
* Unlike standard Collection which loads all documents:
|
|
825
|
+
* - Only document IDs are kept in memory
|
|
826
|
+
* - Documents are loaded on-demand with LRU caching
|
|
827
|
+
* - Queries that scan all documents still need to load from disk
|
|
828
|
+
*/
|
|
829
|
+
declare class LazyCollection<T extends Document> {
|
|
830
|
+
private readonly name;
|
|
831
|
+
private readonly storage;
|
|
832
|
+
private readonly queryEngine;
|
|
833
|
+
private readonly idGenerator;
|
|
834
|
+
private readonly schema?;
|
|
835
|
+
constructor(name: string, storage: LazyStorage, options?: {
|
|
836
|
+
idGenerator?: () => string;
|
|
837
|
+
schema?: SchemaValidator$1<T>;
|
|
838
|
+
});
|
|
839
|
+
/**
|
|
840
|
+
* Validate a document against the schema (if defined)
|
|
841
|
+
*/
|
|
842
|
+
private validate;
|
|
843
|
+
/**
|
|
844
|
+
* Insert a single document
|
|
845
|
+
*/
|
|
846
|
+
insert(doc: Omit<T, '_id'> & {
|
|
847
|
+
_id?: string;
|
|
848
|
+
}): Promise<T>;
|
|
849
|
+
/**
|
|
850
|
+
* Insert without duplicate check (faster)
|
|
851
|
+
*/
|
|
852
|
+
insertFast(doc: Omit<T, '_id'> & {
|
|
853
|
+
_id?: string;
|
|
854
|
+
}): Promise<T>;
|
|
855
|
+
/**
|
|
856
|
+
* Insert multiple documents
|
|
857
|
+
*/
|
|
858
|
+
insertMany(docs: (Omit<T, '_id'> & {
|
|
859
|
+
_id?: string;
|
|
860
|
+
})[]): Promise<T[]>;
|
|
861
|
+
/**
|
|
862
|
+
* Find documents matching a query
|
|
863
|
+
* Note: This loads documents from disk for filtering
|
|
864
|
+
*/
|
|
865
|
+
find(query?: Query<T>, options?: FindOptions<T>): Promise<T[]>;
|
|
866
|
+
/**
|
|
867
|
+
* Find a single document matching a query
|
|
868
|
+
*/
|
|
869
|
+
findOne(query: Query<T>): Promise<T | null>;
|
|
870
|
+
/**
|
|
871
|
+
* Find a document by ID - optimized, uses cache
|
|
872
|
+
*/
|
|
873
|
+
findById(id: string): Promise<T | null>;
|
|
874
|
+
/**
|
|
875
|
+
* Count documents matching a query
|
|
876
|
+
*/
|
|
877
|
+
count(query?: Query<T>): Promise<number>;
|
|
878
|
+
/**
|
|
879
|
+
* Update documents matching a query
|
|
880
|
+
*/
|
|
881
|
+
update(query: Query<T>, update: UpdateOperators<T> | Partial<T>): Promise<number>;
|
|
882
|
+
/**
|
|
883
|
+
* Update a single document matching a query
|
|
884
|
+
*/
|
|
885
|
+
updateOne(query: Query<T>, update: UpdateOperators<T> | Partial<T>): Promise<T | null>;
|
|
886
|
+
/**
|
|
887
|
+
* Update a document by ID - optimized, uses cache
|
|
888
|
+
*/
|
|
889
|
+
updateById(id: string, update: UpdateOperators<T> | Partial<T>): Promise<T | null>;
|
|
890
|
+
/**
|
|
891
|
+
* Delete documents matching a query
|
|
892
|
+
*/
|
|
893
|
+
delete(query: Query<T>): Promise<number>;
|
|
894
|
+
/**
|
|
895
|
+
* Delete a single document matching a query
|
|
896
|
+
*/
|
|
897
|
+
deleteOne(query: Query<T>): Promise<T | null>;
|
|
898
|
+
/**
|
|
899
|
+
* Delete a document by ID - optimized
|
|
900
|
+
*/
|
|
901
|
+
deleteById(id: string): Promise<T | null>;
|
|
902
|
+
/**
|
|
903
|
+
* Get all documents
|
|
904
|
+
*/
|
|
905
|
+
getAll(): Promise<T[]>;
|
|
906
|
+
/**
|
|
907
|
+
* Clear all documents
|
|
908
|
+
*/
|
|
909
|
+
clear(): Promise<void>;
|
|
910
|
+
/**
|
|
911
|
+
* Drop the collection
|
|
912
|
+
*/
|
|
913
|
+
drop(): Promise<void>;
|
|
914
|
+
/**
|
|
915
|
+
* Flush pending writes
|
|
916
|
+
*/
|
|
917
|
+
flush(): Promise<void>;
|
|
918
|
+
/**
|
|
919
|
+
* Get collection name
|
|
920
|
+
*/
|
|
921
|
+
getName(): string;
|
|
922
|
+
/**
|
|
923
|
+
* Apply update operators to a document
|
|
924
|
+
*/
|
|
925
|
+
private applyUpdate;
|
|
926
|
+
/**
|
|
927
|
+
* Sort documents
|
|
928
|
+
*/
|
|
929
|
+
private sortDocuments;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Collection type union for all modes
|
|
655
934
|
*/
|
|
656
|
-
type AnyCollection<T extends Document> = Collection<T> | HighConcurrencyCollection<T>;
|
|
935
|
+
type AnyCollection<T extends Document> = Collection<T> | HighConcurrencyCollection<T> | LazyCollection<T>;
|
|
657
936
|
/**
|
|
658
937
|
* Stats returned by getStats() in high-concurrency mode
|
|
659
938
|
*/
|
|
@@ -665,16 +944,19 @@ interface HighConcurrencyStats {
|
|
|
665
944
|
/**
|
|
666
945
|
* JsonDB - A lightweight JSON-based database for Node.js and Electron
|
|
667
946
|
*
|
|
668
|
-
* Supports
|
|
947
|
+
* Supports three modes:
|
|
669
948
|
* - Standard mode: Single-file storage per collection (default)
|
|
670
949
|
* - High-concurrency mode: Partitioned storage with write batching (opt-in)
|
|
950
|
+
* - Lazy loading mode: Memory-efficient with document-level LRU caching (opt-in)
|
|
671
951
|
*/
|
|
672
952
|
declare class JsonDB {
|
|
673
953
|
private readonly options;
|
|
674
954
|
private readonly storage;
|
|
675
955
|
private readonly hcStorage;
|
|
956
|
+
private readonly lazyStorage;
|
|
676
957
|
private readonly collections;
|
|
677
958
|
private readonly isHighConcurrency;
|
|
959
|
+
private readonly isLazyLoading;
|
|
678
960
|
private connected;
|
|
679
961
|
/**
|
|
680
962
|
* Create a new JsonDB instance
|
|
@@ -682,13 +964,17 @@ declare class JsonDB {
|
|
|
682
964
|
*/
|
|
683
965
|
constructor(options: JsonDBOptions);
|
|
684
966
|
/**
|
|
685
|
-
* Get the standard storage (throws if in HC mode)
|
|
967
|
+
* Get the standard storage (throws if in HC or lazy mode)
|
|
686
968
|
*/
|
|
687
969
|
private getStorage;
|
|
688
970
|
/**
|
|
689
|
-
* Get the high-concurrency storage (throws if in standard mode)
|
|
971
|
+
* Get the high-concurrency storage (throws if in standard or lazy mode)
|
|
690
972
|
*/
|
|
691
973
|
private getHCStorage;
|
|
974
|
+
/**
|
|
975
|
+
* Get the lazy storage (throws if in standard or HC mode)
|
|
976
|
+
*/
|
|
977
|
+
private getLazyStorage;
|
|
692
978
|
/**
|
|
693
979
|
* Connect to the database (initialize storage)
|
|
694
980
|
*/
|
|
@@ -698,10 +984,10 @@ declare class JsonDB {
|
|
|
698
984
|
*/
|
|
699
985
|
close(): Promise<void>;
|
|
700
986
|
/**
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
987
|
+
* Get or create a collection
|
|
988
|
+
* @param name Collection name
|
|
989
|
+
* @param options Collection options (including optional Zod schema)
|
|
990
|
+
*/
|
|
705
991
|
collection<T extends Document>(name: string, options?: {
|
|
706
992
|
schema?: {
|
|
707
993
|
safeParse(data: unknown): {
|
|
@@ -1049,4 +1335,4 @@ declare function generateId(_length?: number): string;
|
|
|
1049
1335
|
*/
|
|
1050
1336
|
declare function isValidId(id: unknown): id is string;
|
|
1051
1337
|
|
|
1052
|
-
export { Collection, type CollectionData, CollectionError, type CollectionOptions, type ComparisonOperators, type Document, DocumentNotFoundError, DuplicateKeyError, type FindOptions, HighConcurrencyCollection, type HighConcurrencyOptions, HighConcurrencyStorage, JsonDB, JsonDBError, type JsonDBOptions, PartitionManager, type Query, type QueryField, type SchemaIssue, type SchemaValidator$1 as SchemaValidator, type Sort, type SortOrder, type StorageAdapter, StorageError, type UpdateOperators, ValidationError, WorkerPool, WriteQueue, generateId, isValidId, parallelLimit };
|
|
1338
|
+
export { Collection, type CollectionData, CollectionError, type CollectionOptions, type ComparisonOperators, type Document, DocumentNotFoundError, DuplicateKeyError, type FindOptions, HighConcurrencyCollection, type HighConcurrencyOptions, HighConcurrencyStorage, JsonDB, JsonDBError, type JsonDBOptions, PartitionManager, type Projection, type Query, type QueryField, type SchemaIssue, type SchemaValidator$1 as SchemaValidator, type Sort, type SortOrder, type StorageAdapter, StorageError, type UpdateOperators, ValidationError, WorkerPool, WriteQueue, generateId, isValidId, parallelLimit };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var d=require('fs'),$=require('path'),bson=require('bson');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var d__namespace=/*#__PURE__*/_interopNamespace(d);var $__namespace=/*#__PURE__*/_interopNamespace($);var g=class extends Error{constructor(t){super(t),this.name="JsonDBError",Error.captureStackTrace?.(this,this.constructor);}},Q=class extends g{constructor(t,e){super(e?`Document with id "${e}" not found in collection "${t}"`:`Document not found in collection "${t}"`),this.name="DocumentNotFoundError";}},y=class extends g{constructor(t,e){super(`Duplicate key error: document with id "${e}" already exists in collection "${t}"`),this.name="DuplicateKeyError";}},w=class extends g{collectionName;issues;field;value;constructor(t,e,i,r){let n=e.map(s=>`${s.path.join(".")}: ${s.message}`).join("; ");super(`Validation failed for collection "${t}": ${n}`),this.name="ValidationError",this.collectionName=t,this.issues=e,this.field=i,this.value=r;}},f=class extends g{cause;constructor(t,e){super(t),this.name="StorageError",this.cause=e;}},P=class extends g{constructor(t){super(t),this.name="CollectionError";}};var b=class{dataDir;fileExtension;prettyPrint;cache=new Map;locks=new Map;constructor(t){this.dataDir=$__namespace.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.ensureDirectory();}ensureDirectory(){try{d__namespace.existsSync(this.dataDir)||d__namespace.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t){return $__namespace.join(this.dataDir,`${t}${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(r=>{e=r;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async read(t){if(this.cache.has(t))return this.cache.get(t);let e=this.getFilePath(t);try{if(!d__namespace.existsSync(e))return null;let i=await d__namespace.promises.readFile(e,"utf-8"),r=JSON.parse(i);return this.cache.set(t,r),r}catch(i){throw new f(`Failed to read collection "${t}"`,i)}}async write(t,e){let i=this.getFilePath(t),r=`${i}.tmp.${Date.now()}`,n=await this.acquireLock(t);try{e.updatedAt=new Date().toISOString();let s=this.prettyPrint?JSON.stringify(e,null,2):JSON.stringify(e);await d__namespace.promises.writeFile(r,s,"utf-8"),await d__namespace.promises.rename(r,i),this.cache.set(t,e);}catch(s){try{d__namespace.existsSync(r)&&await d__namespace.promises.unlink(r);}catch{}throw new f(`Failed to write collection "${t}"`,s)}finally{n();}}async exists(t){let e=this.getFilePath(t);return d__namespace.existsSync(e)}async delete(t){let e=this.getFilePath(t),i=await this.acquireLock(t);try{d__namespace.existsSync(e)&&await d__namespace.promises.unlink(e),this.cache.delete(t);}catch(r){throw new f(`Failed to delete collection "${t}"`,r)}finally{i();}}async list(){try{return (await d__namespace.promises.readdir(this.dataDir)).filter(e=>e.endsWith(this.fileExtension)).map(e=>e.slice(0,-this.fileExtension.length))}catch(t){throw new f("Failed to list collections",t)}}clearCache(t){t?this.cache.delete(t):this.cache.clear();}getDataDir(){return this.dataDir}};function v(a){return new bson.ObjectId().toHexString()}function H(a){return typeof a!="string"||a.length===0||a.length>64?false:a.length===24?bson.ObjectId.isValid(a):true}function l(a){if(a===null||typeof a!="object")return a;if(a instanceof Date)return new Date(a.getTime());if(a instanceof RegExp)return new RegExp(a.source,a.flags);if(Array.isArray(a))return a.map(e=>l(e));let t={};for(let e in a)Object.prototype.hasOwnProperty.call(a,e)&&(t[e]=l(a[e]));return t}function A(a,t){let e=t.split("."),i=a;for(let r of e){if(i==null||typeof i!="object")return;i=i[r];}return i}function W(a,t,e){let i=t.split("."),r=a;for(let n=0;n<i.length-1;n++){let s=i[n];(!(s in r)||typeof r[s]!="object"||r[s]===null)&&(r[s]={}),r=r[s];}r[i[i.length-1]]=e;}function q(a,t){let e=t.split("."),i=a;for(let n=0;n<e.length-1;n++){let s=e[n];if(!(s in i)||typeof i[s]!="object"||i[s]===null)return false;i=i[s];}let r=e[e.length-1];return r in i?(delete i[r],true):false}function _(a){return typeof a=="object"&&a!==null&&!Array.isArray(a)&&!(a instanceof Date)&&!(a instanceof RegExp)}var T=class{filter(t,e){return !e||Object.keys(e).length===0?t:t.filter(i=>this.matches(i,e))}matches(t,e){if("$and"in e&&e.$and)return e.$and.every(i=>this.matches(t,i));if("$or"in e&&e.$or)return e.$or.some(i=>this.matches(t,i));if("$not"in e&&e.$not)return !this.matches(t,e.$not);for(let[i,r]of Object.entries(e)){if(i.startsWith("$"))continue;let n=A(t,i);if(!this.matchesCondition(n,r))return false}return true}matchesCondition(t,e){if(!_(e)||!this.hasOperators(e))return this.isEqual(t,e);let i=e;if("$eq"in i&&!this.isEqual(t,i.$eq)||"$ne"in i&&this.isEqual(t,i.$ne)||"$gt"in i&&!this.compareValues(t,i.$gt,">")||"$gte"in i&&!this.compareValues(t,i.$gte,">=")||"$lt"in i&&!this.compareValues(t,i.$lt,"<")||"$lte"in i&&!this.compareValues(t,i.$lte,"<=")||"$in"in i&&Array.isArray(i.$in)&&!i.$in.some(r=>this.isEqual(t,r))||"$nin"in i&&Array.isArray(i.$nin)&&i.$nin.some(r=>this.isEqual(t,r)))return false;if("$exists"in i){let r=t!=null;if(i.$exists!==r)return false}return !("$regex"in i&&(typeof t!="string"||!(i.$regex instanceof RegExp?i.$regex:new RegExp(i.$regex)).test(t))||"$startsWith"in i&&(typeof t!="string"||typeof i.$startsWith!="string"||!t.startsWith(i.$startsWith))||"$endsWith"in i&&(typeof t!="string"||typeof i.$endsWith!="string"||!t.endsWith(i.$endsWith))||"$contains"in i&&(!Array.isArray(t)||!t.some(r=>this.isEqual(r,i.$contains))))}hasOperators(t){return Object.keys(t).some(e=>e.startsWith("$"))}isEqual(t,e){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(typeof t!=typeof e)return false;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)&&Array.isArray(e))return t.length!==e.length?false:t.every((i,r)=>this.isEqual(i,e[r]));if(typeof t=="object"&&typeof e=="object"){let i=Object.keys(t),r=Object.keys(e);return i.length!==r.length?false:i.every(n=>this.isEqual(t[n],e[n]))}return false}compareValues(t,e,i){if(typeof t=="number"&&typeof e=="number")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(typeof t=="string"&&typeof e=="string")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(t instanceof Date&&e instanceof Date)switch(i){case ">":return t.getTime()>e.getTime();case ">=":return t.getTime()>=e.getTime();case "<":return t.getTime()<e.getTime();case "<=":return t.getTime()<=e.getTime()}return false}};var D=class{name;storage;queryEngine;autoSave;saveDebounce;idGenerator;schema;saveTimeout=null;pendingSave=null;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new T,this.autoSave=i.autoSave??true,this.saveDebounce=i.saveDebounce??0,this.idGenerator=i.idGenerator??v,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new w(this.name,e.error.issues);return e.data}async getData(){let t=await this.storage.read(this.name);if(!t){let e={name:this.name,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};return await this.storage.write(this.name,e),e}return t}async save(t){if(this.autoSave){if(this.saveDebounce>0)return this.saveTimeout&&clearTimeout(this.saveTimeout),new Promise(e=>{this.saveTimeout=setTimeout(async()=>{await this.storage.write(this.name,t),this.saveTimeout=null,e();},this.saveDebounce);});await this.storage.write(this.name,t);}}async flush(){this.saveTimeout&&(clearTimeout(this.saveTimeout),this.saveTimeout=null),this.pendingSave&&await this.pendingSave;let t=await this.getData();await this.storage.write(this.name,t);}async insert(t){let e=await this.getData(),i=t._id||this.idGenerator();if(e.documents.some(s=>s._id===i))throw new y(this.name,i);let r={...l(t),_id:i},n=this.validate(r);return e.documents.push(n),await this.save(e),l(n)}async insertFast(t){let e=await this.getData(),i=t._id||this.idGenerator(),r={...l(t),_id:i},n=this.validate(r);return e.documents.push(n),await this.save(e),l(n)}async insertMany(t){if(t.length===0)return [];let e=await this.getData(),i=[],r=new Set(e.documents.map(n=>n._id));for(let n of t){let s=n._id||this.idGenerator();if(r.has(s))throw new y(this.name,s);r.add(s);let o={...l(n),_id:s},c=this.validate(o);e.documents.push(c),i.push(l(c));}return await this.save(e),i}async find(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);return e?.sort&&(r=this.sortDocuments(r,e.sort)),e?.skip&&e.skip>0&&(r=r.slice(e.skip)),e?.limit&&e.limit>0&&(r=r.slice(0,e.limit)),r.map(n=>l(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){return this.findOne({_id:t})}async count(t){let e=await this.getData();return this.queryEngine.filter(e.documents,t).length}async update(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);if(r.length===0)return 0;for(let n of r)this.applyUpdate(n,e);return await this.save(i),r.length}async updateOne(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);if(r.length===0)return null;let n=r[0];return this.applyUpdate(n,e),await this.save(i),l(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.getData(),i=e.documents.length;e.documents=e.documents.filter(n=>!this.queryEngine.matches(n,t));let r=i-e.documents.length;return r>0&&await this.save(e),r}async deleteOne(t){let e=await this.getData(),i=e.documents.findIndex(n=>this.queryEngine.matches(n,t));if(i===-1)return null;let[r]=e.documents.splice(i,1);return await this.save(e),l(r)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){let t=await this.getData();t.documents=[],await this.save(t);}async drop(){await this.storage.delete(this.name);}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let r=e;if(r.$set)for(let[n,s]of Object.entries(r.$set))n!=="_id"&&W(t,n,s);if(r.$unset)for(let n of Object.keys(r.$unset))n!=="_id"&&q(t,n);if(r.$inc)for(let[n,s]of Object.entries(r.$inc)){let o=t[n];typeof o=="number"&&typeof s=="number"&&(t[n]=o+s);}if(r.$push)for(let[n,s]of Object.entries(r.$push)){let o=t[n];Array.isArray(o)&&o.push(s);}if(r.$pull)for(let[n,s]of Object.entries(r.$pull)){let o=t[n];if(Array.isArray(o)){let c=o.findIndex(u=>JSON.stringify(u)===JSON.stringify(s));c!==-1&&o.splice(c,1);}}if(r.$addToSet)for(let[n,s]of Object.entries(r.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(u=>JSON.stringify(u)===JSON.stringify(s))||o.push(s));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((r,n)=>{for(let[s,o]of i){let c=r[s],u=n[s],h=0;if(c===u?h=0:c==null?h=1:u==null?h=-1:typeof c=="number"&&typeof u=="number"?h=c-u:typeof c=="string"&&typeof u=="string"?h=c.localeCompare(u):c instanceof Date&&u instanceof Date?h=c.getTime()-u.getTime():h=String(c).localeCompare(String(u)),h!==0)return h*(o===-1||o==="desc"?-1:1)}return 0})}};var C=class{batchSize;flushInterval;coalesceWrites;batchProcessor;queue=new Map;flushTimer=null;pendingFlush=null;totalQueued=0;isShuttingDown=false;constructor(t,e={}){this.batchSize=e.batchSize??1e3,this.flushInterval=e.flushInterval??100,this.coalesceWrites=e.coalesceWrites??true,this.batchProcessor=t;}async enqueue(t){if(this.isShuttingDown)throw new Error("WriteQueue is shutting down, no new writes accepted");return new Promise((e,i)=>{let r={operation:t,resolve:e,reject:i,timestamp:Date.now()},n=t.collectionName,s=this.queue.get(n);s||(s=[],this.queue.set(n,s)),!(this.coalesceWrites&&this.tryCoalesce(n,r))&&(s.push(r),this.totalQueued++,this.scheduleFlush(),this.totalQueued>=this.batchSize&&this.flush().catch(i));})}tryCoalesce(t,e){let i=this.queue.get(t);if(!i||i.length===0)return false;let r=e.operation;if(r.type==="update")for(let n=i.length-1;n>=0;n--){let s=i[n].operation;if(s.type==="update"&&s.documentId===r.documentId){s.changes={...s.changes,...r.changes};let o=i[n].resolve;return i[n].resolve=()=>{o(),e.resolve();},true}}if(r.type==="delete")for(let n=i.length-1;n>=0;n--){let s=i[n].operation;(s.type==="insert"&&s.document._id===r.documentId||s.type==="update"&&s.documentId===r.documentId)&&(i.splice(n,1),this.totalQueued--);}if(r.type==="clear"||r.type==="fullWrite"){for(let n of i)n.resolve();i.length=0,this.totalQueued-=i.length;}return false}scheduleFlush(){this.flushTimer===null&&(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush().catch(t=>{console.error("WriteQueue flush error:",t);});},this.flushInterval));}async flush(){if(this.pendingFlush)return this.pendingFlush;if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.totalQueued===0)return;let t=this.queue;this.queue=new Map,this.totalQueued=0;let e=new Map;for(let[i,r]of t)e.set(i,r.map(n=>n.operation));this.pendingFlush=this.processBatch(t,e),await this.pendingFlush,this.pendingFlush=null;}async processBatch(t,e){try{await this.batchProcessor(e);for(let i of t.values())for(let r of i)r.resolve();}catch(i){for(let r of t.values())for(let n of r)n.reject(i);}}async shutdown(){this.isShuttingDown=true,await this.flush();}pending(){return this.totalQueued}isEmpty(){return this.totalQueued===0}isClosing(){return this.isShuttingDown}};var x=class{dataDir;partitionCount;prettyPrint;fileExtension;cache=new Map;locks=new Map;constructor(t,e={}){this.dataDir=$__namespace.resolve(t),this.partitionCount=e.partitionCount??16,this.prettyPrint=e.prettyPrint??true,this.fileExtension=e.fileExtension??".json",this.ensureDirectory();}ensureDirectory(){try{d__namespace.existsSync(this.dataDir)||d__namespace.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getPartitionIndex(t){let e=0;for(let i=0;i<t.length;i++){let r=t.charCodeAt(i);e=(e<<5)-e+r,e=e&e;}return Math.abs(e)%this.partitionCount}getPartitionFileName(t,e){return `${t}_p${e.toString().padStart(3,"0")}${this.fileExtension}`}getPartitionFilePath(t,e){return $__namespace.join(this.dataDir,this.getPartitionFileName(t,e))}getCacheKey(t,e){return `${t}:${e}`}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(r=>{e=r;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async readPartition(t,e){let i=this.getCacheKey(t,e);if(this.cache.has(i))return this.cache.get(i);let r=this.getPartitionFilePath(t,e);try{if(!d__namespace.existsSync(r))return null;let n=await d__namespace.promises.readFile(r,"utf-8"),s=JSON.parse(n);return this.cache.set(i,s),s}catch(n){throw new f(`Failed to read partition ${e} for collection "${t}"`,n)}}async writePartition(t,e,i){let r=this.getPartitionFilePath(t,e),n=`${r}.tmp.${Date.now()}`,s=this.getCacheKey(t,e),o=await this.acquireLock(s);try{i.updatedAt=new Date().toISOString();let c=this.prettyPrint?JSON.stringify(i,null,2):JSON.stringify(i);await d__namespace.promises.writeFile(n,c,"utf-8"),await d__namespace.promises.rename(n,r),this.cache.set(s,i);}catch(c){try{d__namespace.existsSync(n)&&await d__namespace.promises.unlink(n);}catch{}throw new f(`Failed to write partition ${e} for collection "${t}"`,c)}finally{o();}}async readAllPartitions(t){let e=new Map,i=[];for(let r=0;r<this.partitionCount;r++)i.push(this.readPartition(t,r).then(n=>{n&&e.set(r,n);}));return await Promise.all(i),e}async writePartitions(t,e){let i=[];for(let[r,n]of e)i.push(this.writePartition(t,r,n));await Promise.all(i);}async initializePartitions(t){let e=[];for(let i=0;i<this.partitionCount;i++)if(!d__namespace.existsSync(this.getPartitionFilePath(t,i))){let n={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};e.push(this.writePartition(t,i,n));}await Promise.all(e);}async getPartitionInfo(t){let e=await this.readAllPartitions(t),i=[];for(let r=0;r<this.partitionCount;r++){let n=e.get(r);i.push({name:this.getPartitionFileName(t,r),partitionIndex:r,documentCount:n?.documents.length??0});}return i}async getAllDocuments(t){let e=await this.readAllPartitions(t),i=[];for(let r of e.values())i.push(...r.documents);return i}async findById(t,e){let i=this.getPartitionIndex(e),r=await this.readPartition(t,i);return r?r.documents.find(n=>n._id===e)??null:null}async deleteCollection(t){let e=[];for(let i=0;i<this.partitionCount;i++){let r=this.getPartitionFilePath(t,i),n=this.getCacheKey(t,i);e.push((async()=>{let s=await this.acquireLock(n);try{d__namespace.existsSync(r)&&await d__namespace.promises.unlink(r),this.cache.delete(n);}finally{s();}})());}await Promise.all(e);}clearCache(t){if(t)for(let e=0;e<this.partitionCount;e++)this.cache.delete(this.getCacheKey(t,e));else this.cache.clear();}getPartitionCount(){return this.partitionCount}async listCollections(){try{let t=await d__namespace.promises.readdir(this.dataDir),e=new Set,i=new RegExp(`^(.+)_p\\d{3}\\${this.fileExtension}$`);for(let r of t){let n=r.match(i);n&&e.add(n[1]);}return Array.from(e)}catch(t){throw new f("Failed to list partitioned collections",t)}}};var S=class{maxConcurrent;maxQueueSize;queue=[];activeCount=0;completedCount=0;failedCount=0;isShuttingDown=false;constructor(t={}){this.maxConcurrent=t.maxConcurrent??4,this.maxQueueSize=t.maxQueueSize??1e4;}async submit(t,e=0){if(this.isShuttingDown)throw new Error("WorkerPool is shutting down, no new tasks accepted");if(this.queue.length>=this.maxQueueSize)throw new Error("WorkerPool queue is full, task rejected (backpressure)");return new Promise((i,r)=>{let n={task:t,resolve:i,reject:r,priority:e},s=false;for(let o=0;o<this.queue.length;o++)if(e>this.queue[o].priority){this.queue.splice(o,0,n),s=true;break}s||this.queue.push(n),this.processNext();})}async submitAll(t,e=0){let i=t.map(r=>this.submit(r,e));return Promise.all(i)}async*submitStream(t,e=0){let i=t.map(r=>this.submit(r,e));for(let r of i)yield await r;}processNext(){if(this.activeCount>=this.maxConcurrent||this.queue.length===0)return;let t=this.queue.shift();t&&(this.activeCount++,t.task().then(e=>{this.completedCount++,t.resolve(e);}).catch(e=>{this.failedCount++,t.reject(e);}).finally(()=>{this.activeCount--,this.processNext();}));}async drain(){return new Promise(t=>{let e=()=>{this.activeCount===0&&this.queue.length===0?t():setImmediate(e);};e();})}async shutdown(){this.isShuttingDown=true,await this.drain();}getStats(){return {activeWorkers:this.activeCount,queuedTasks:this.queue.length,completedTasks:this.completedCount,failedTasks:this.failedCount}}isIdle(){return this.activeCount===0&&this.queue.length===0}isClosing(){return this.isShuttingDown}queueSize(){return this.queue.length}activeWorkers(){return this.activeCount}};async function I(a,t,e){let i=[],r=0;async function n(){let o=r++;o>=a.length||(i[o]=await e(a[o],o),await n());}let s=Array(Math.min(t,a.length)).fill(null).map(()=>n());return await Promise.all(s),i}var R={partitions:16,batchSize:1e3,flushInterval:100,maxConcurrentIO:4,coalesceWrites:true},k=class{partitionManager;writeQueue;workerPool;options;constructor(t){let e=t.highConcurrency;this.options={...R,...e},this.partitionManager=new x(t.dataDir,{partitionCount:this.options.partitions,prettyPrint:t.prettyPrint??true,fileExtension:t.fileExtension??".json"}),this.workerPool=new S({maxConcurrent:this.options.maxConcurrentIO});let i=this.processBatch.bind(this);this.writeQueue=new C(i,{batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,coalesceWrites:this.options.coalesceWrites});}async processBatch(t){let e=Array.from(t.entries());await I(e,this.options.maxConcurrentIO,async([i,r])=>{await this.processCollectionOperations(i,r);});}async processCollectionOperations(t,e){let i=new Map,r=n=>{let s=i.get(n);return s||(s=[],i.set(n,s)),s};for(let n of e)if(n.type==="fullWrite"||n.type==="clear")for(let s=0;s<this.partitionManager.getPartitionCount();s++)r(s).push(n);else if(n.type==="insert"){let s=this.partitionManager.getPartitionIndex(n.document._id);r(s).push(n);}else if(n.type==="update"||n.type==="delete"){let s=this.partitionManager.getPartitionIndex(n.documentId);r(s).push(n);}else if(n.type==="bulkInsert")for(let s of n.documents){let o=this.partitionManager.getPartitionIndex(s._id);r(o).push({type:"insert",collectionName:t,document:s});}else if(n.type==="bulkUpdate"||n.type==="bulkDelete")for(let s of n.documentIds){let o=this.partitionManager.getPartitionIndex(s);n.type==="bulkUpdate"?r(o).push({type:"update",collectionName:t,documentId:s,changes:n.changes}):r(o).push({type:"delete",collectionName:t,documentId:s});}await I(Array.from(i.entries()),this.options.maxConcurrentIO,async([n,s])=>{await this.processPartitionOperations(t,n,s);});}async processPartitionOperations(t,e,i){let r=await this.partitionManager.readPartition(t,e);r||(r={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});for(let n of i)switch(n.type){case "insert":r.documents.push(n.document);break;case "update":{let s=r.documents.findIndex(o=>o._id===n.documentId);s!==-1&&Object.assign(r.documents[s],n.changes);break}case "delete":{let s=r.documents.findIndex(o=>o._id===n.documentId);s!==-1&&r.documents.splice(s,1);break}case "clear":r.documents=[];break;case "fullWrite":{let s=n.data.documents.filter(o=>this.partitionManager.getPartitionIndex(o._id)===e);r.documents=s;break}}await this.partitionManager.writePartition(t,e,r);}async insert(t,e){await this.writeQueue.enqueue({type:"insert",collectionName:t,document:e});}async insertMany(t,e){await this.writeQueue.enqueue({type:"bulkInsert",collectionName:t,documents:e});}async update(t,e,i){await this.writeQueue.enqueue({type:"update",collectionName:t,documentId:e,changes:i});}async delete(t,e){await this.writeQueue.enqueue({type:"delete",collectionName:t,documentId:e});}async clear(t){await this.writeQueue.enqueue({type:"clear",collectionName:t});}async findById(t,e){return await this.writeQueue.flush(),this.partitionManager.findById(t,e)}async readAll(t){return await this.writeQueue.flush(),this.partitionManager.getAllDocuments(t)}async exists(t){return (await this.partitionManager.listCollections()).includes(t)}async initializeCollection(t){await this.partitionManager.initializePartitions(t);}async deleteCollection(t){await this.writeQueue.flush(),await this.partitionManager.deleteCollection(t);}async listCollections(){return this.partitionManager.listCollections()}async flush(){await this.writeQueue.flush();}async shutdown(){await this.writeQueue.shutdown(),await this.workerPool.shutdown();}pendingWrites(){return this.writeQueue.pending()}getStats(){return {pendingWrites:this.writeQueue.pending(),workerPool:this.workerPool.getStats(),partitions:this.options.partitions}}clearCache(t){this.partitionManager.clearCache(t);}};var O=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new T,this.idGenerator=i.idGenerator??v,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new w(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(await this.storage.findById(this.name,e))throw new y(this.name,e);let r={...l(t),_id:e},n=this.validate(r);return await this.storage.insert(this.name,n),n}async insertFast(t){let e=t._id||this.idGenerator(),i={...l(t),_id:e},r=this.validate(i);return await this.storage.insert(this.name,r),r}async insertMany(t){if(t.length===0)return [];let e=[],i=[];for(let r of t){let n=r._id||this.idGenerator(),s={...l(r),_id:n},o=this.validate(s);i.push(o),e.push(l(o));}return await this.storage.insertMany(this.name,i),e}async find(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);return e?.sort&&(r=this.sortDocuments(r,e.sort)),e?.skip&&e.skip>0&&(r=r.slice(e.skip)),e?.limit&&e.limit>0&&(r=r.slice(0,e.limit)),r.map(n=>l(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.findById(this.name,t);return e?l(e):null}async count(t){let e=await this.storage.readAll(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);if(r.length===0)return 0;for(let n of r){let s=this.getUpdateChanges(n,e);await this.storage.update(this.name,n._id,s);}return r.length}async updateOne(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);if(r.length===0)return null;let n=r[0],s=this.getUpdateChanges(n,e);return await this.storage.update(this.name,n._id,s),Object.assign(n,s),l(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;for(let r of i)await this.storage.delete(this.name,r._id);return i.length}async deleteOne(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let r=i[0];return await this.storage.delete(this.name,r._id),l(r)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){await this.storage.clear(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}getUpdateChanges(t,e){if(!Object.keys(e).some(s=>s.startsWith("$")))return e;let r=e,n={};if(r.$set)for(let[s,o]of Object.entries(r.$set))s!=="_id"&&(n[s]=o);if(r.$inc)for(let[s,o]of Object.entries(r.$inc)){let c=t[s];typeof c=="number"&&typeof o=="number"&&(n[s]=c+o);}if(r.$push)for(let[s,o]of Object.entries(r.$push)){let c=t[s];Array.isArray(c)&&(n[s]=[...c,o]);}if(r.$pull)for(let[s,o]of Object.entries(r.$pull)){let c=t[s];Array.isArray(c)&&(n[s]=c.filter(u=>JSON.stringify(u)!==JSON.stringify(o)));}if(r.$addToSet)for(let[s,o]of Object.entries(r.$addToSet)){let c=t[s];Array.isArray(c)&&(c.some(h=>JSON.stringify(h)===JSON.stringify(o))?n[s]=c:n[s]=[...c,o]);}return n}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((r,n)=>{for(let[s,o]of i){let c=r[s],u=n[s],h=0;if(c===u?h=0:c==null?h=1:u==null?h=-1:typeof c=="number"&&typeof u=="number"?h=c-u:typeof c=="string"&&typeof u=="string"?h=c.localeCompare(u):c instanceof Date&&u instanceof Date?h=c.getTime()-u.getTime():h=String(c).localeCompare(String(u)),h!==0)return h*(o===-1||o==="desc"?-1:1)}return 0})}};var V={autoSave:true,saveDebounce:0,prettyPrint:true,fileExtension:".json"},F=class{options;storage;hcStorage;collections=new Map;isHighConcurrency;connected=false;constructor(t){this.options={...V,...t},this.isHighConcurrency=t.highConcurrency?.enabled??false,this.isHighConcurrency?(this.storage=null,this.hcStorage=new k(this.options)):(this.storage=new b(this.options),this.hcStorage=null);}getStorage(){if(this.storage===null)throw new Error("Storage is not available in high-concurrency mode");return this.storage}getHCStorage(){if(this.hcStorage===null)throw new Error("HighConcurrencyStorage is not available in standard mode");return this.hcStorage}async connect(){if(!this.connected){if(this.isHighConcurrency){let t=await this.getHCStorage().listCollections();for(let e of t)this.getOrCreateCollection(e);}else {let t=await this.getStorage().list();for(let e of t)this.getOrCreateCollection(e);}this.connected=true;}}async close(){if(this.connected){for(let t of this.collections.values())await t.flush();this.isHighConcurrency&&this.hcStorage&&await this.hcStorage.shutdown(),this.collections.clear(),this.storage&&this.storage.clearCache(),this.hcStorage&&this.hcStorage.clearCache(),this.connected=false;}}collection(t,e){if(!t||typeof t!="string")throw new P("Collection name must be a non-empty string");if(!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(t))throw new P("Collection name must start with a letter or underscore and contain only letters, numbers, underscores, and hyphens");return this.getOrCreateCollection(t,e)}getOrCreateCollection(t,e){if(this.collections.has(t))return this.collections.get(t);if(this.isHighConcurrency){let i=new O(t,this.getHCStorage(),{schema:e?.schema});this.collections.set(t,i);}else {let i=new D(t,this.getStorage(),{autoSave:this.options.autoSave,saveDebounce:this.options.saveDebounce,schema:e?.schema});this.collections.set(t,i);}return this.collections.get(t)}async hasCollection(t){return this.isHighConcurrency?this.getHCStorage().exists(t):this.getStorage().exists(t)}async listCollections(){return this.isHighConcurrency?this.getHCStorage().listCollections():this.getStorage().list()}async dropCollection(t){let e=this.collections.get(t);e?(await e.drop(),this.collections.delete(t)):this.isHighConcurrency?await this.getHCStorage().deleteCollection(t):await this.getStorage().delete(t);}async drop(){if(this.isHighConcurrency){let t=this.getHCStorage(),e=await t.listCollections();for(let i of e)await t.deleteCollection(i);}else {let t=this.getStorage(),e=await t.list();for(let i of e)await t.delete(i);}this.collections.clear(),this.storage?.clearCache(),this.hcStorage?.clearCache();}getDataDir(){return this.isHighConcurrency?this.options.dataDir:this.getStorage().getDataDir()}isConnected(){return this.connected}isHighConcurrencyMode(){return this.isHighConcurrency}getStats(){return !this.isHighConcurrency||!this.hcStorage?null:this.hcStorage.getStats()}async flush(){for(let t of this.collections.values())await t.flush();}};exports.Collection=D;exports.CollectionError=P;exports.DocumentNotFoundError=Q;exports.DuplicateKeyError=y;exports.HighConcurrencyCollection=O;exports.HighConcurrencyStorage=k;exports.JsonDB=F;exports.JsonDBError=g;exports.PartitionManager=x;exports.StorageError=f;exports.ValidationError=w;exports.WorkerPool=S;exports.WriteQueue=C;exports.generateId=v;exports.isValidId=H;exports.parallelLimit=I;
|
|
1
|
+
'use strict';var d=require('fs'),S=require('path'),bson=require('bson');function _interopNamespace(e){if(e&&e.__esModule)return e;var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var d__namespace=/*#__PURE__*/_interopNamespace(d);var S__namespace=/*#__PURE__*/_interopNamespace(S);var D=class extends Error{constructor(t){super(t),this.name="JsonDBError",Error.captureStackTrace?.(this,this.constructor);}},j=class extends D{constructor(t,e){super(e?`Document with id "${e}" not found in collection "${t}"`:`Document not found in collection "${t}"`),this.name="DocumentNotFoundError";}},y=class extends D{constructor(t,e){super(`Duplicate key error: document with id "${e}" already exists in collection "${t}"`),this.name="DuplicateKeyError";}},w=class extends D{collectionName;issues;field;value;constructor(t,e,i,s){let n=e.map(r=>`${r.path.join(".")}: ${r.message}`).join("; ");super(`Validation failed for collection "${t}": ${n}`),this.name="ValidationError",this.collectionName=t,this.issues=e,this.field=i,this.value=s;}},f=class extends D{cause;constructor(t,e){super(t),this.name="StorageError",this.cause=e;}},v=class extends D{constructor(t){super(t),this.name="CollectionError";}};var F=class{dataDir;fileExtension;prettyPrint;cache=new Map;locks=new Map;constructor(t){this.dataDir=S__namespace.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.ensureDirectory();}ensureDirectory(){try{d__namespace.existsSync(this.dataDir)||d__namespace.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t){return S__namespace.join(this.dataDir,`${t}${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async read(t){if(this.cache.has(t))return this.cache.get(t);let e=this.getFilePath(t);try{if(!d__namespace.existsSync(e))return null;let i=await d__namespace.promises.readFile(e,"utf-8"),s=JSON.parse(i);return this.cache.set(t,s),s}catch(i){throw new f(`Failed to read collection "${t}"`,i)}}async write(t,e){let i=this.getFilePath(t),s=`${i}.tmp.${Date.now()}`,n=await this.acquireLock(t);try{e.updatedAt=new Date().toISOString();let r=this.prettyPrint?JSON.stringify(e,null,2):JSON.stringify(e);await d__namespace.promises.writeFile(s,r,"utf-8"),await d__namespace.promises.rename(s,i),this.cache.set(t,e);}catch(r){try{d__namespace.existsSync(s)&&await d__namespace.promises.unlink(s);}catch{}throw new f(`Failed to write collection "${t}"`,r)}finally{n();}}async exists(t){let e=this.getFilePath(t);return d__namespace.existsSync(e)}async delete(t){let e=this.getFilePath(t),i=await this.acquireLock(t);try{d__namespace.existsSync(e)&&await d__namespace.promises.unlink(e),this.cache.delete(t);}catch(s){throw new f(`Failed to delete collection "${t}"`,s)}finally{i();}}async list(){try{return (await d__namespace.promises.readdir(this.dataDir)).filter(e=>e.endsWith(this.fileExtension)).map(e=>e.slice(0,-this.fileExtension.length))}catch(t){throw new f("Failed to list collections",t)}}clearCache(t){t?this.cache.delete(t):this.cache.clear();}getDataDir(){return this.dataDir}};function P(c){return new bson.ObjectId().toHexString()}function B(c){return typeof c!="string"||c.length===0||c.length>64?false:c.length===24?bson.ObjectId.isValid(c):true}function h(c){if(c===null||typeof c!="object")return c;if(c instanceof Date)return new Date(c.getTime());if(c instanceof RegExp)return new RegExp(c.source,c.flags);if(Array.isArray(c))return c.map(e=>h(e));let t={};for(let e in c)Object.prototype.hasOwnProperty.call(c,e)&&(t[e]=h(c[e]));return t}function R(c,t){let e=t.split("."),i=c;for(let s of e){if(i==null||typeof i!="object")return;i=i[s];}return i}function x(c,t,e){let i=t.split("."),s=c;for(let n=0;n<i.length-1;n++){let r=i[n];(!(r in s)||typeof s[r]!="object"||s[r]===null)&&(s[r]={}),s=s[r];}s[i[i.length-1]]=e;}function k(c,t){let e=t.split("."),i=c;for(let n=0;n<e.length-1;n++){let r=e[n];if(!(r in i)||typeof i[r]!="object"||i[r]===null)return false;i=i[r];}let s=e[e.length-1];return s in i?(delete i[s],true):false}function V(c){return typeof c=="object"&&c!==null&&!Array.isArray(c)&&!(c instanceof Date)&&!(c instanceof RegExp)}function b(c,t){if(!t||Object.keys(t).length===0)return c;let e=Object.entries(t).filter(([a])=>a!=="_id"),i=t._id,s=e.some(([,a])=>a===1||a===true),n=e.some(([,a])=>a===0||a===false);if(s&&n)throw new Error("Cannot mix inclusion and exclusion in projection");let r=s,o={};if(r){i!==0&&i!==false&&"_id"in c&&(o._id=c._id);for(let[l,u]of e)(u===1||u===true)&&l in c&&(o[l]=c[l]);}else {let a=new Set(e.filter(([,u])=>u===0||u===false).map(([u])=>u));(i===0||i===false)&&a.add("_id");for(let u of Object.keys(c))a.has(u)||(o[u]=c[u]);}return o}var T=class{filter(t,e){return !e||Object.keys(e).length===0?t:t.filter(i=>this.matches(i,e))}matches(t,e){if("$and"in e&&e.$and)return e.$and.every(i=>this.matches(t,i));if("$or"in e&&e.$or)return e.$or.some(i=>this.matches(t,i));if("$not"in e&&e.$not)return !this.matches(t,e.$not);for(let[i,s]of Object.entries(e)){if(i.startsWith("$"))continue;let n=R(t,i);if(!this.matchesCondition(n,s))return false}return true}matchesCondition(t,e){if(!V(e)||!this.hasOperators(e))return this.isEqual(t,e);let i=e;if("$eq"in i&&!this.isEqual(t,i.$eq)||"$ne"in i&&this.isEqual(t,i.$ne)||"$gt"in i&&!this.compareValues(t,i.$gt,">")||"$gte"in i&&!this.compareValues(t,i.$gte,">=")||"$lt"in i&&!this.compareValues(t,i.$lt,"<")||"$lte"in i&&!this.compareValues(t,i.$lte,"<=")||"$in"in i&&Array.isArray(i.$in)&&!i.$in.some(s=>this.isEqual(t,s))||"$nin"in i&&Array.isArray(i.$nin)&&i.$nin.some(s=>this.isEqual(t,s)))return false;if("$exists"in i){let s=t!=null;if(i.$exists!==s)return false}if("$regex"in i&&(typeof t!="string"||!(i.$regex instanceof RegExp?i.$regex:new RegExp(i.$regex)).test(t))||"$startsWith"in i&&(typeof t!="string"||typeof i.$startsWith!="string"||!t.startsWith(i.$startsWith))||"$endsWith"in i&&(typeof t!="string"||typeof i.$endsWith!="string"||!t.endsWith(i.$endsWith))||"$contains"in i&&(!Array.isArray(t)||!t.some(s=>this.isEqual(s,i.$contains)))||"$all"in i&&Array.isArray(i.$all)&&(!Array.isArray(t)||!i.$all.every(s=>t.some(n=>this.isEqual(n,s)))))return false;if("$elemMatch"in i&&i.$elemMatch){if(!Array.isArray(t))return false;let s=i.$elemMatch;if(!t.some(r=>typeof r!="object"||r===null?false:Object.entries(s).every(([o,a])=>{let l=r[o];return this.matchesCondition(l,a)})))return false}if("$size"in i&&typeof i.$size=="number"&&(!Array.isArray(t)||t.length!==i.$size))return false;if("$type"in i&&i.$type){let s=i.$type,n;if(t===null?n="null":t===void 0?n="undefined":Array.isArray(t)?n="array":n=typeof t,n!==s)return false}if("$mod"in i&&Array.isArray(i.$mod)&&i.$mod.length===2){if(typeof t!="number")return false;let[s,n]=i.$mod;if(t%s!==n)return false}return true}hasOperators(t){return Object.keys(t).some(e=>e.startsWith("$"))}isEqual(t,e){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(typeof t!=typeof e)return false;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)&&Array.isArray(e))return t.length!==e.length?false:t.every((i,s)=>this.isEqual(i,e[s]));if(typeof t=="object"&&typeof e=="object"){let i=Object.keys(t),s=Object.keys(e);return i.length!==s.length?false:i.every(n=>this.isEqual(t[n],e[n]))}return false}compareValues(t,e,i){if(typeof t=="number"&&typeof e=="number")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(typeof t=="string"&&typeof e=="string")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(t instanceof Date&&e instanceof Date)switch(i){case ">":return t.getTime()>e.getTime();case ">=":return t.getTime()>=e.getTime();case "<":return t.getTime()<e.getTime();case "<=":return t.getTime()<=e.getTime()}return false}};var O=class{name;storage;queryEngine;autoSave;saveDebounce;idGenerator;schema;saveTimeout=null;pendingSave=null;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new T,this.autoSave=i.autoSave??true,this.saveDebounce=i.saveDebounce??0,this.idGenerator=i.idGenerator??P,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new w(this.name,e.error.issues);return e.data}async getData(){let t=await this.storage.read(this.name);if(!t){let e={name:this.name,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};return await this.storage.write(this.name,e),e}return t}async save(t){if(this.autoSave){if(this.saveDebounce>0)return this.saveTimeout&&clearTimeout(this.saveTimeout),new Promise(e=>{this.saveTimeout=setTimeout(async()=>{await this.storage.write(this.name,t),this.saveTimeout=null,e();},this.saveDebounce);});await this.storage.write(this.name,t);}}async flush(){this.saveTimeout&&(clearTimeout(this.saveTimeout),this.saveTimeout=null),this.pendingSave&&await this.pendingSave;let t=await this.getData();await this.storage.write(this.name,t);}async insert(t){let e=await this.getData(),i=t._id||this.idGenerator();if(e.documents.some(r=>r._id===i))throw new y(this.name,i);let s={...h(t),_id:i},n=this.validate(s);return e.documents.push(n),await this.save(e),h(n)}async insertFast(t){let e=await this.getData(),i=t._id||this.idGenerator(),s={...h(t),_id:i},n=this.validate(s);return e.documents.push(n),await this.save(e),h(n)}async insertMany(t){if(t.length===0)return [];let e=await this.getData(),i=[],s=new Set(e.documents.map(n=>n._id));for(let n of t){let r=n._id||this.idGenerator();if(s.has(r))throw new y(this.name,r);s.add(r);let o={...h(n),_id:r},a=this.validate(o);e.documents.push(a),i.push(h(a));}return await this.save(e),i}async find(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),e?.projection?s.map(n=>b(h(n),e.projection)):s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){return this.findOne({_id:t})}async count(t){let e=await this.getData();return this.queryEngine.filter(e.documents,t).length}async update(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);if(s.length===0)return 0;for(let n of s)this.applyUpdate(n,e);return await this.save(i),s.length}async updateOne(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);if(s.length===0)return null;let n=s[0];return this.applyUpdate(n,e),await this.save(i),h(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.getData(),i=e.documents.length;e.documents=e.documents.filter(n=>!this.queryEngine.matches(n,t));let s=i-e.documents.length;return s>0&&await this.save(e),s}async deleteOne(t){let e=await this.getData(),i=e.documents.findIndex(n=>this.queryEngine.matches(n,t));if(i===-1)return null;let[s]=e.documents.splice(i,1);return await this.save(e),h(s)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){let t=await this.getData();t.documents=[],await this.save(t);}async drop(){await this.storage.delete(this.name);}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let s=e;if(s.$set)for(let[n,r]of Object.entries(s.$set))n!=="_id"&&x(t,n,r);if(s.$unset)for(let n of Object.keys(s.$unset))n!=="_id"&&k(t,n);if(s.$inc)for(let[n,r]of Object.entries(s.$inc)){let o=t[n];typeof o=="number"&&typeof r=="number"&&(t[n]=o+r);}if(s.$push)for(let[n,r]of Object.entries(s.$push)){let o=t[n];Array.isArray(o)&&o.push(r);}if(s.$pull)for(let[n,r]of Object.entries(s.$pull)){let o=t[n];if(Array.isArray(o)){let a=o.findIndex(l=>JSON.stringify(l)===JSON.stringify(r));a!==-1&&o.splice(a,1);}}if(s.$addToSet)for(let[n,r]of Object.entries(s.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(l=>JSON.stringify(l)===JSON.stringify(r))||o.push(r));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var $=class{batchSize;flushInterval;coalesceWrites;batchProcessor;queue=new Map;flushTimer=null;pendingFlush=null;totalQueued=0;isShuttingDown=false;constructor(t,e={}){this.batchSize=e.batchSize??1e3,this.flushInterval=e.flushInterval??100,this.coalesceWrites=e.coalesceWrites??true,this.batchProcessor=t;}async enqueue(t){if(this.isShuttingDown)throw new Error("WriteQueue is shutting down, no new writes accepted");return new Promise((e,i)=>{let s={operation:t,resolve:e,reject:i,timestamp:Date.now()},n=t.collectionName,r=this.queue.get(n);r||(r=[],this.queue.set(n,r)),!(this.coalesceWrites&&this.tryCoalesce(n,s))&&(r.push(s),this.totalQueued++,this.scheduleFlush(),this.totalQueued>=this.batchSize&&this.flush().catch(i));})}tryCoalesce(t,e){let i=this.queue.get(t);if(!i||i.length===0)return false;let s=e.operation;if(s.type==="update")for(let n=i.length-1;n>=0;n--){let r=i[n].operation;if(r.type==="update"&&r.documentId===s.documentId){r.changes={...r.changes,...s.changes};let o=i[n].resolve;return i[n].resolve=()=>{o(),e.resolve();},true}}if(s.type==="delete")for(let n=i.length-1;n>=0;n--){let r=i[n].operation;(r.type==="insert"&&r.document._id===s.documentId||r.type==="update"&&r.documentId===s.documentId)&&(i.splice(n,1),this.totalQueued--);}if(s.type==="clear"||s.type==="fullWrite"){for(let n of i)n.resolve();i.length=0,this.totalQueued-=i.length;}return false}scheduleFlush(){this.flushTimer===null&&(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush().catch(t=>{console.error("WriteQueue flush error:",t);});},this.flushInterval));}async flush(){if(this.pendingFlush)return this.pendingFlush;if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.totalQueued===0)return;let t=this.queue;this.queue=new Map,this.totalQueued=0;let e=new Map;for(let[i,s]of t)e.set(i,s.map(n=>n.operation));this.pendingFlush=this.processBatch(t,e),await this.pendingFlush,this.pendingFlush=null;}async processBatch(t,e){try{await this.batchProcessor(e);for(let i of t.values())for(let s of i)s.resolve();}catch(i){for(let s of t.values())for(let n of s)n.reject(i);}}async shutdown(){this.isShuttingDown=true,await this.flush();}pending(){return this.totalQueued}isEmpty(){return this.totalQueued===0}isClosing(){return this.isShuttingDown}};var A=class{dataDir;partitionCount;prettyPrint;fileExtension;cache=new Map;locks=new Map;constructor(t,e={}){this.dataDir=S__namespace.resolve(t),this.partitionCount=e.partitionCount??16,this.prettyPrint=e.prettyPrint??true,this.fileExtension=e.fileExtension??".json",this.ensureDirectory();}ensureDirectory(){try{d__namespace.existsSync(this.dataDir)||d__namespace.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getPartitionIndex(t){let e=0;for(let i=0;i<t.length;i++){let s=t.charCodeAt(i);e=(e<<5)-e+s,e=e&e;}return Math.abs(e)%this.partitionCount}getPartitionFileName(t,e){return `${t}_p${e.toString().padStart(3,"0")}${this.fileExtension}`}getPartitionFilePath(t,e){return S__namespace.join(this.dataDir,this.getPartitionFileName(t,e))}getCacheKey(t,e){return `${t}:${e}`}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async readPartition(t,e){let i=this.getCacheKey(t,e);if(this.cache.has(i))return this.cache.get(i);let s=this.getPartitionFilePath(t,e);try{if(!d__namespace.existsSync(s))return null;let n=await d__namespace.promises.readFile(s,"utf-8"),r=JSON.parse(n);return this.cache.set(i,r),r}catch(n){throw new f(`Failed to read partition ${e} for collection "${t}"`,n)}}async writePartition(t,e,i){let s=this.getPartitionFilePath(t,e),n=`${s}.tmp.${Date.now()}`,r=this.getCacheKey(t,e),o=await this.acquireLock(r);try{i.updatedAt=new Date().toISOString();let a=this.prettyPrint?JSON.stringify(i,null,2):JSON.stringify(i);await d__namespace.promises.writeFile(n,a,"utf-8"),await d__namespace.promises.rename(n,s),this.cache.set(r,i);}catch(a){try{d__namespace.existsSync(n)&&await d__namespace.promises.unlink(n);}catch{}throw new f(`Failed to write partition ${e} for collection "${t}"`,a)}finally{o();}}async readAllPartitions(t){let e=new Map,i=[];for(let s=0;s<this.partitionCount;s++)i.push(this.readPartition(t,s).then(n=>{n&&e.set(s,n);}));return await Promise.all(i),e}async writePartitions(t,e){let i=[];for(let[s,n]of e)i.push(this.writePartition(t,s,n));await Promise.all(i);}async initializePartitions(t){let e=[];for(let i=0;i<this.partitionCount;i++)if(!d__namespace.existsSync(this.getPartitionFilePath(t,i))){let n={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};e.push(this.writePartition(t,i,n));}await Promise.all(e);}async getPartitionInfo(t){let e=await this.readAllPartitions(t),i=[];for(let s=0;s<this.partitionCount;s++){let n=e.get(s);i.push({name:this.getPartitionFileName(t,s),partitionIndex:s,documentCount:n?.documents.length??0});}return i}async getAllDocuments(t){let e=await this.readAllPartitions(t),i=[];for(let s of e.values())i.push(...s.documents);return i}async findById(t,e){let i=this.getPartitionIndex(e),s=await this.readPartition(t,i);return s?s.documents.find(n=>n._id===e)??null:null}async deleteCollection(t){let e=[];for(let i=0;i<this.partitionCount;i++){let s=this.getPartitionFilePath(t,i),n=this.getCacheKey(t,i);e.push((async()=>{let r=await this.acquireLock(n);try{d__namespace.existsSync(s)&&await d__namespace.promises.unlink(s),this.cache.delete(n);}finally{r();}})());}await Promise.all(e);}clearCache(t){if(t)for(let e=0;e<this.partitionCount;e++)this.cache.delete(this.getCacheKey(t,e));else this.cache.clear();}getPartitionCount(){return this.partitionCount}async listCollections(){try{let t=await d__namespace.promises.readdir(this.dataDir),e=new Set,i=new RegExp(`^(.+)_p\\d{3}\\${this.fileExtension}$`);for(let s of t){let n=s.match(i);n&&e.add(n[1]);}return Array.from(e)}catch(t){throw new f("Failed to list partitioned collections",t)}}};var E=class{maxConcurrent;maxQueueSize;queue=[];activeCount=0;completedCount=0;failedCount=0;isShuttingDown=false;constructor(t={}){this.maxConcurrent=t.maxConcurrent??4,this.maxQueueSize=t.maxQueueSize??1e4;}async submit(t,e=0){if(this.isShuttingDown)throw new Error("WorkerPool is shutting down, no new tasks accepted");if(this.queue.length>=this.maxQueueSize)throw new Error("WorkerPool queue is full, task rejected (backpressure)");return new Promise((i,s)=>{let n={task:t,resolve:i,reject:s,priority:e},r=false;for(let o=0;o<this.queue.length;o++)if(e>this.queue[o].priority){this.queue.splice(o,0,n),r=true;break}r||this.queue.push(n),this.processNext();})}async submitAll(t,e=0){let i=t.map(s=>this.submit(s,e));return Promise.all(i)}async*submitStream(t,e=0){let i=t.map(s=>this.submit(s,e));for(let s of i)yield await s;}processNext(){if(this.activeCount>=this.maxConcurrent||this.queue.length===0)return;let t=this.queue.shift();t&&(this.activeCount++,t.task().then(e=>{this.completedCount++,t.resolve(e);}).catch(e=>{this.failedCount++,t.reject(e);}).finally(()=>{this.activeCount--,this.processNext();}));}async drain(){return new Promise(t=>{let e=()=>{this.activeCount===0&&this.queue.length===0?t():setImmediate(e);};e();})}async shutdown(){this.isShuttingDown=true,await this.drain();}getStats(){return {activeWorkers:this.activeCount,queuedTasks:this.queue.length,completedTasks:this.completedCount,failedTasks:this.failedCount}}isIdle(){return this.activeCount===0&&this.queue.length===0}isClosing(){return this.isShuttingDown}queueSize(){return this.queue.length}activeWorkers(){return this.activeCount}};async function q(c,t,e){let i=[],s=0;async function n(){let o=s++;o>=c.length||(i[o]=await e(c[o],o),await n());}let r=Array(Math.min(t,c.length)).fill(null).map(()=>n());return await Promise.all(r),i}var U={partitions:16,batchSize:1e3,flushInterval:100,maxConcurrentIO:4,coalesceWrites:true},I=class{partitionManager;writeQueue;workerPool;options;constructor(t){let e=t.highConcurrency;this.options={...U,...e},this.partitionManager=new A(t.dataDir,{partitionCount:this.options.partitions,prettyPrint:t.prettyPrint??true,fileExtension:t.fileExtension??".json"}),this.workerPool=new E({maxConcurrent:this.options.maxConcurrentIO});let i=this.processBatch.bind(this);this.writeQueue=new $(i,{batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,coalesceWrites:this.options.coalesceWrites});}async processBatch(t){let e=Array.from(t.entries());await q(e,this.options.maxConcurrentIO,async([i,s])=>{await this.processCollectionOperations(i,s);});}async processCollectionOperations(t,e){let i=new Map,s=n=>{let r=i.get(n);return r||(r=[],i.set(n,r)),r};for(let n of e)if(n.type==="fullWrite"||n.type==="clear")for(let r=0;r<this.partitionManager.getPartitionCount();r++)s(r).push(n);else if(n.type==="insert"){let r=this.partitionManager.getPartitionIndex(n.document._id);s(r).push(n);}else if(n.type==="update"||n.type==="delete"){let r=this.partitionManager.getPartitionIndex(n.documentId);s(r).push(n);}else if(n.type==="bulkInsert")for(let r of n.documents){let o=this.partitionManager.getPartitionIndex(r._id);s(o).push({type:"insert",collectionName:t,document:r});}else if(n.type==="bulkUpdate"||n.type==="bulkDelete")for(let r of n.documentIds){let o=this.partitionManager.getPartitionIndex(r);n.type==="bulkUpdate"?s(o).push({type:"update",collectionName:t,documentId:r,changes:n.changes}):s(o).push({type:"delete",collectionName:t,documentId:r});}await q(Array.from(i.entries()),this.options.maxConcurrentIO,async([n,r])=>{await this.processPartitionOperations(t,n,r);});}async processPartitionOperations(t,e,i){let s=await this.partitionManager.readPartition(t,e);s||(s={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});for(let n of i)switch(n.type){case "insert":s.documents.push(n.document);break;case "update":{let r=s.documents.findIndex(o=>o._id===n.documentId);r!==-1&&Object.assign(s.documents[r],n.changes);break}case "delete":{let r=s.documents.findIndex(o=>o._id===n.documentId);r!==-1&&s.documents.splice(r,1);break}case "clear":s.documents=[];break;case "fullWrite":{let r=n.data.documents.filter(o=>this.partitionManager.getPartitionIndex(o._id)===e);s.documents=r;break}}await this.partitionManager.writePartition(t,e,s);}async insert(t,e){await this.writeQueue.enqueue({type:"insert",collectionName:t,document:e});}async insertMany(t,e){await this.writeQueue.enqueue({type:"bulkInsert",collectionName:t,documents:e});}async update(t,e,i){await this.writeQueue.enqueue({type:"update",collectionName:t,documentId:e,changes:i});}async delete(t,e){await this.writeQueue.enqueue({type:"delete",collectionName:t,documentId:e});}async clear(t){await this.writeQueue.enqueue({type:"clear",collectionName:t});}async findById(t,e){return await this.writeQueue.flush(),this.partitionManager.findById(t,e)}async readAll(t){return await this.writeQueue.flush(),this.partitionManager.getAllDocuments(t)}async exists(t){return (await this.partitionManager.listCollections()).includes(t)}async initializeCollection(t){await this.partitionManager.initializePartitions(t);}async deleteCollection(t){await this.writeQueue.flush(),await this.partitionManager.deleteCollection(t);}async listCollections(){return this.partitionManager.listCollections()}async flush(){await this.writeQueue.flush();}async shutdown(){await this.writeQueue.shutdown(),await this.workerPool.shutdown();}pendingWrites(){return this.writeQueue.pending()}getStats(){return {pendingWrites:this.writeQueue.pending(),workerPool:this.workerPool.getStats(),partitions:this.options.partitions}}clearCache(t){this.partitionManager.clearCache(t);}};var _=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new T,this.idGenerator=i.idGenerator??P,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new w(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(await this.storage.findById(this.name,e))throw new y(this.name,e);let s={...h(t),_id:e},n=this.validate(s);return await this.storage.insert(this.name,n),n}async insertFast(t){let e=t._id||this.idGenerator(),i={...h(t),_id:e},s=this.validate(i);return await this.storage.insert(this.name,s),s}async insertMany(t){if(t.length===0)return [];let e=[],i=[];for(let s of t){let n=s._id||this.idGenerator(),r={...h(s),_id:n},o=this.validate(r);i.push(o),e.push(h(o));}return await this.storage.insertMany(this.name,i),e}async find(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.findById(this.name,t);return e?h(e):null}async count(t){let e=await this.storage.readAll(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return 0;for(let n of s){let r=this.getUpdateChanges(n,e);await this.storage.update(this.name,n._id,r);}return s.length}async updateOne(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return null;let n=s[0],r=this.getUpdateChanges(n,e);return await this.storage.update(this.name,n._id,r),Object.assign(n,r),h(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;for(let s of i)await this.storage.delete(this.name,s._id);return i.length}async deleteOne(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let s=i[0];return await this.storage.delete(this.name,s._id),h(s)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){await this.storage.clear(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}getUpdateChanges(t,e){if(!Object.keys(e).some(r=>r.startsWith("$")))return e;let s=e,n={};if(s.$set)for(let[r,o]of Object.entries(s.$set))r!=="_id"&&(n[r]=o);if(s.$inc)for(let[r,o]of Object.entries(s.$inc)){let a=t[r];typeof a=="number"&&typeof o=="number"&&(n[r]=a+o);}if(s.$push)for(let[r,o]of Object.entries(s.$push)){let a=t[r];Array.isArray(a)&&(n[r]=[...a,o]);}if(s.$pull)for(let[r,o]of Object.entries(s.$pull)){let a=t[r];Array.isArray(a)&&(n[r]=a.filter(l=>JSON.stringify(l)!==JSON.stringify(o)));}if(s.$addToSet)for(let[r,o]of Object.entries(s.$addToSet)){let a=t[r];Array.isArray(a)&&(a.some(u=>JSON.stringify(u)===JSON.stringify(o))?n[r]=a:n[r]=[...a,o]);}return n}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var C=class{capacity;cache;constructor(t){this.capacity=t,this.cache=new Map;}get(t){if(!this.cache.has(t))return;let e=this.cache.get(t);return this.cache.delete(t),this.cache.set(t,e),e}set(t,e){if(this.cache.has(t))this.cache.delete(t);else if(this.cache.size>=this.capacity){let i=this.cache.keys().next().value;i!==void 0&&this.cache.delete(i);}this.cache.set(t,e);}has(t){return this.cache.has(t)}delete(t){return this.cache.delete(t)}clear(){this.cache.clear();}get size(){return this.cache.size}keys(){return this.cache.keys()}values(){return this.cache.values()}entries(){return this.cache.entries()}forEach(t){this.cache.forEach(t);}};var W=class{dataDir;fileExtension;prettyPrint;cacheSize;chunkSize;documentCache;collections;locks;constructor(t){this.dataDir=S__namespace.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.cacheSize=t.lazyLoading?.cacheSize??1e3,this.chunkSize=t.lazyLoading?.chunkSize??1e4,this.documentCache=new Map,this.collections=new Map,this.locks=new Map,this.ensureDirectory();}ensureDirectory(){try{d__namespace.existsSync(this.dataDir)||d__namespace.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t,e){return e!==void 0&&this.chunkSize>0?S__namespace.join(this.dataDir,`${t}_chunk${e}${this.fileExtension}`):S__namespace.join(this.dataDir,`${t}${this.fileExtension}`)}getIndexFilePath(t){return S__namespace.join(this.dataDir,`${t}.index${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async initCollection(t){if(this.collections.has(t))return;let e=this.getFilePath(t),i=this.getIndexFilePath(t);if(d__namespace.existsSync(i))try{let s=await d__namespace.promises.readFile(i,"utf-8"),n=JSON.parse(s),r={name:t,createdAt:n.createdAt,updatedAt:n.updatedAt,index:new Map(n.ids.map((o,a)=>[o,{id:o,offset:a}])),count:n.ids.length,dirty:!1};this.collections.set(t,r),this.documentCache.set(t,new C(this.cacheSize));return}catch{}if(d__namespace.existsSync(e))try{let s=await d__namespace.promises.readFile(e,"utf-8"),n=JSON.parse(s),r=new Map;n.documents.forEach((u,Q)=>{r.set(u._id,{id:u._id,offset:Q});});let o={name:t,createdAt:n.createdAt,updatedAt:n.updatedAt,index:r,count:n.documents.length,dirty:!1};this.collections.set(t,o),this.documentCache.set(t,new C(this.cacheSize));let a=this.documentCache.get(t),l=Math.min(n.documents.length,this.cacheSize);for(let u=0;u<l;u++)a.set(n.documents[u]._id,n.documents[u]);await this.saveIndex(t);}catch(s){throw new f(`Failed to initialize collection "${t}"`,s)}else {let s={name:t,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),index:new Map,count:0,dirty:false};this.collections.set(t,s),this.documentCache.set(t,new C(this.cacheSize));}}async saveIndex(t){let e=this.collections.get(t);if(!e)return;let i=this.getIndexFilePath(t),s={name:t,createdAt:e.createdAt,updatedAt:e.updatedAt,ids:Array.from(e.index.keys())},n=this.prettyPrint?JSON.stringify(s,null,2):JSON.stringify(s);await d__namespace.promises.writeFile(i,n,"utf-8");}getCount(t){return this.collections.get(t)?.count??0}hasDocument(t,e){return this.collections.get(t)?.index.has(e)??false}getDocumentIds(t){let e=this.collections.get(t);return e?Array.from(e.index.keys()):[]}async getDocument(t,e){await this.initCollection(t);let i=this.collections.get(t);if(!i||!i.index.has(e))return null;let s=this.documentCache.get(t),n=s.get(e);if(n)return n;let r=await this.loadDocumentFromDisk(t,e);return r&&s.set(e,r),r}async loadDocumentFromDisk(t,e){let i=this.getFilePath(t);try{let s=await d__namespace.promises.readFile(i,"utf-8");return JSON.parse(s).documents.find(r=>r._id===e)??null}catch{return null}}async getDocuments(t,e){await this.initCollection(t);let i=this.documentCache.get(t),s=[],n=[];for(let r of e){let o=i.get(r);o?s.push(o):n.push(r);}if(n.length>0){let r=await this.loadDocumentsFromDisk(t,n);for(let o of r)i.set(o._id,o),s.push(o);}return s}async loadDocumentsFromDisk(t,e){let i=this.getFilePath(t),s=new Set(e);try{let n=await d__namespace.promises.readFile(i,"utf-8");return JSON.parse(n).documents.filter(o=>s.has(o._id))}catch{return []}}async getAllDocuments(t){await this.initCollection(t);let e=this.getFilePath(t);try{if(!d__namespace.existsSync(e))return [];let i=await d__namespace.promises.readFile(e,"utf-8"),s=JSON.parse(i),n=this.documentCache.get(t);for(let r of s.documents)n.set(r._id,r);return s.documents}catch{return []}}async insertDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);s.index.set(e._id,{id:e._id,offset:s.count}),s.count++,s.updatedAt=new Date().toISOString(),s.dirty=!0,n.set(e._id,e),await this.persistCollection(t);}finally{i();}}async insertDocuments(t,e){if(e.length===0)return;await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);for(let r of e)s.index.set(r._id,{id:r._id,offset:s.count}),s.count++,n.set(r._id,r);s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async updateDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);if(!s.index.has(e._id))return;n.set(e._id,e),s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async deleteDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);if(!s.index.has(e))return;s.index.delete(e),s.count--,n.delete(e),s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async deleteDocuments(t,e){if(e.length===0)return;await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);for(let r of e)s.index.has(r)&&(s.index.delete(r),s.count--,n.delete(r));s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async clearCollection(t){await this.initCollection(t);let e=await this.acquireLock(t);try{let i=this.collections.get(t),s=this.documentCache.get(t);i.index.clear(),i.count=0,s.clear(),i.updatedAt=new Date().toISOString(),i.dirty=!0,await this.persistCollection(t);}finally{e();}}async persistCollection(t){let e=this.collections.get(t);if(!e)return;let i=this.getFilePath(t),s=`${i}.tmp.${Date.now()}`;try{let n=await this.buildDocumentArray(t),r={name:t,documents:n,createdAt:e.createdAt,updatedAt:e.updatedAt},o=this.prettyPrint?JSON.stringify(r,null,2):JSON.stringify(r);await d__namespace.promises.writeFile(s,o,"utf-8"),await d__namespace.promises.rename(s,i),await this.saveIndex(t),e.dirty=!1;}catch(n){try{d__namespace.existsSync(s)&&await d__namespace.promises.unlink(s);}catch{}throw new f(`Failed to persist collection "${t}"`,n)}}async buildDocumentArray(t){let e=this.collections.get(t);if(!e||e.count===0)return [];let i=this.getFilePath(t),s=[];try{if(d__namespace.existsSync(i)){let a=await d__namespace.promises.readFile(i,"utf-8");s=JSON.parse(a).documents;}}catch{}let n=new Map;for(let a of s)n.set(a._id,a);this.documentCache.get(t).forEach((a,l)=>{n.set(l,a);});let o=[];for(let a of e.index.keys()){let l=n.get(a);l&&o.push(l);}return o}async deleteCollection(t){let e=await this.acquireLock(t);try{this.collections.delete(t),this.documentCache.delete(t);let i=this.getFilePath(t),s=this.getIndexFilePath(t);d__namespace.existsSync(i)&&await d__namespace.promises.unlink(i),d__namespace.existsSync(s)&&await d__namespace.promises.unlink(s);}finally{e();}}async exists(t){if(this.collections.has(t))return true;let e=this.getFilePath(t);return d__namespace.existsSync(e)}async list(){try{let t=await d__namespace.promises.readdir(this.dataDir),e=new Set;for(let i of t)i.includes(".index")||i.includes("_chunk")||i.endsWith(this.fileExtension)&&e.add(i.slice(0,-this.fileExtension.length));return Array.from(e)}catch{return []}}getStats(){let t=0;for(let e of this.documentCache.values())t+=e.size;return {collections:this.collections.size,cachedDocuments:t,cacheSize:this.cacheSize}}clearCache(t){if(t)this.documentCache.get(t)?.clear();else for(let e of this.documentCache.values())e.clear();}async flush(){for(let[t,e]of this.collections.entries())e.dirty&&await this.persistCollection(t);}getDataDir(){return this.dataDir}};var L=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new T,this.idGenerator=i.idGenerator??P,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new w(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(this.storage.hasDocument(this.name,e))throw new y(this.name,e);let i={...h(t),_id:e},s=this.validate(i);return await this.storage.insertDocument(this.name,s),h(s)}async insertFast(t){let e=t._id||this.idGenerator(),i={...h(t),_id:e},s=this.validate(i);return await this.storage.insertDocument(this.name,s),h(s)}async insertMany(t){if(t.length===0)return [];let e=new Set(this.storage.getDocumentIds(this.name)),i=[];for(let s of t){let n=s._id||this.idGenerator();if(e.has(n))throw new y(this.name,n);e.add(n);let r={...h(s),_id:n},o=this.validate(r);i.push(o);}return await this.storage.insertDocuments(this.name,i),i.map(s=>h(s))}async find(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),e?.projection?s.map(n=>b(h(n),e.projection)):s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.getDocument(this.name,t);return e?h(e):null}async count(t){if(!t||Object.keys(t).length===0)return this.storage.getCount(this.name);let e=await this.storage.getAllDocuments(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return 0;for(let n of s)this.applyUpdate(n,e),await this.storage.updateDocument(this.name,n);return s.length}async updateOne(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return null;let n=s[0];return this.applyUpdate(n,e),await this.storage.updateDocument(this.name,n),h(n)}async updateById(t,e){let i=await this.storage.getDocument(this.name,t);return i?(this.applyUpdate(i,e),await this.storage.updateDocument(this.name,i),h(i)):null}async delete(t){let e=await this.storage.getAllDocuments(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;let s=i.map(n=>n._id);return await this.storage.deleteDocuments(this.name,s),i.length}async deleteOne(t){let e=await this.storage.getAllDocuments(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let s=i[0];return await this.storage.deleteDocument(this.name,s._id),h(s)}async deleteById(t){let e=await this.storage.getDocument(this.name,t);return e?(await this.storage.deleteDocument(this.name,t),h(e)):null}async getAll(){return this.find()}async clear(){await this.storage.clearCollection(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let s=e;if(s.$set)for(let[n,r]of Object.entries(s.$set))n!=="_id"&&x(t,n,r);if(s.$unset)for(let n of Object.keys(s.$unset))n!=="_id"&&k(t,n);if(s.$inc)for(let[n,r]of Object.entries(s.$inc)){let o=t[n];typeof o=="number"&&typeof r=="number"&&(t[n]=o+r);}if(s.$push)for(let[n,r]of Object.entries(s.$push)){let o=t[n];Array.isArray(o)&&o.push(r);}if(s.$pull)for(let[n,r]of Object.entries(s.$pull)){let o=t[n];if(Array.isArray(o)){let a=o.findIndex(l=>JSON.stringify(l)===JSON.stringify(r));a!==-1&&o.splice(a,1);}}if(s.$addToSet)for(let[n,r]of Object.entries(s.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(l=>JSON.stringify(l)===JSON.stringify(r))||o.push(r));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var K={autoSave:true,saveDebounce:0,prettyPrint:true,fileExtension:".json"},H=class{options;storage;hcStorage;lazyStorage;collections=new Map;isHighConcurrency;isLazyLoading;connected=false;constructor(t){if(this.options={...K,...t},this.isHighConcurrency=t.highConcurrency?.enabled??false,this.isLazyLoading=t.lazyLoading?.enabled??false,this.isHighConcurrency&&this.isLazyLoading)throw new Error("Cannot enable both highConcurrency and lazyLoading modes simultaneously");this.isHighConcurrency?(this.storage=null,this.hcStorage=new I(this.options),this.lazyStorage=null):this.isLazyLoading?(this.storage=null,this.hcStorage=null,this.lazyStorage=new W(this.options)):(this.storage=new F(this.options),this.hcStorage=null,this.lazyStorage=null);}getStorage(){if(this.storage===null)throw new Error("Storage is not available in high-concurrency or lazy loading mode");return this.storage}getHCStorage(){if(this.hcStorage===null)throw new Error("HighConcurrencyStorage is not available in standard or lazy loading mode");return this.hcStorage}getLazyStorage(){if(this.lazyStorage===null)throw new Error("LazyStorage is not available in standard or high-concurrency mode");return this.lazyStorage}async connect(){if(!this.connected){if(this.isHighConcurrency){let t=await this.getHCStorage().listCollections();for(let e of t)this.getOrCreateCollection(e);}else if(this.isLazyLoading){let t=await this.getLazyStorage().list();for(let e of t)this.getOrCreateCollection(e);}else {let t=await this.getStorage().list();for(let e of t)this.getOrCreateCollection(e);}this.connected=true;}}async close(){if(this.connected){for(let t of this.collections.values())await t.flush();this.isHighConcurrency&&this.hcStorage&&await this.hcStorage.shutdown(),this.collections.clear(),this.storage&&this.storage.clearCache(),this.hcStorage&&this.hcStorage.clearCache(),this.lazyStorage&&this.lazyStorage.clearCache(),this.connected=false;}}collection(t,e){if(!t||typeof t!="string")throw new v("Collection name must be a non-empty string");if(!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(t))throw new v("Collection name must start with a letter or underscore and contain only letters, numbers, underscores, and hyphens");return this.getOrCreateCollection(t,e)}getOrCreateCollection(t,e){if(this.collections.has(t))return this.collections.get(t);if(this.isHighConcurrency){let i=new _(t,this.getHCStorage(),{schema:e?.schema});this.collections.set(t,i);}else if(this.isLazyLoading){let i=new L(t,this.getLazyStorage(),{schema:e?.schema});this.collections.set(t,i);}else {let i=new O(t,this.getStorage(),{autoSave:this.options.autoSave,saveDebounce:this.options.saveDebounce,schema:e?.schema});this.collections.set(t,i);}return this.collections.get(t)}async hasCollection(t){return this.isHighConcurrency?this.getHCStorage().exists(t):this.getStorage().exists(t)}async listCollections(){return this.isHighConcurrency?this.getHCStorage().listCollections():this.getStorage().list()}async dropCollection(t){let e=this.collections.get(t);e?(await e.drop(),this.collections.delete(t)):this.isHighConcurrency?await this.getHCStorage().deleteCollection(t):await this.getStorage().delete(t);}async drop(){if(this.isHighConcurrency){let t=this.getHCStorage(),e=await t.listCollections();for(let i of e)await t.deleteCollection(i);}else {let t=this.getStorage(),e=await t.list();for(let i of e)await t.delete(i);}this.collections.clear(),this.storage?.clearCache(),this.hcStorage?.clearCache();}getDataDir(){return this.isHighConcurrency?this.options.dataDir:this.getStorage().getDataDir()}isConnected(){return this.connected}isHighConcurrencyMode(){return this.isHighConcurrency}getStats(){return !this.isHighConcurrency||!this.hcStorage?null:this.hcStorage.getStats()}async flush(){for(let t of this.collections.values())await t.flush();}};exports.Collection=O;exports.CollectionError=v;exports.DocumentNotFoundError=j;exports.DuplicateKeyError=y;exports.HighConcurrencyCollection=_;exports.HighConcurrencyStorage=I;exports.JsonDB=H;exports.JsonDBError=D;exports.PartitionManager=A;exports.StorageError=f;exports.ValidationError=w;exports.WorkerPool=E;exports.WriteQueue=$;exports.generateId=P;exports.isValidId=B;exports.parallelLimit=q;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as d from'fs';import*as E from'path';import {ObjectId}from'bson';var y=class extends Error{constructor(t){super(t),this.name="JsonDBError",Error.captureStackTrace?.(this,this.constructor);}},A=class extends y{constructor(t,e){super(e?`Document with id "${e}" not found in collection "${t}"`:`Document not found in collection "${t}"`),this.name="DocumentNotFoundError";}},w=class extends y{constructor(t,e){super(`Duplicate key error: document with id "${e}" already exists in collection "${t}"`),this.name="DuplicateKeyError";}},T=class extends y{collectionName;issues;field;value;constructor(t,e,i,r){let n=e.map(s=>`${s.path.join(".")}: ${s.message}`).join("; ");super(`Validation failed for collection "${t}": ${n}`),this.name="ValidationError",this.collectionName=t,this.issues=e,this.field=i,this.value=r;}},f=class extends y{cause;constructor(t,e){super(t),this.name="StorageError",this.cause=e;}},v=class extends y{constructor(t){super(t),this.name="CollectionError";}};var $=class{dataDir;fileExtension;prettyPrint;cache=new Map;locks=new Map;constructor(t){this.dataDir=E.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.ensureDirectory();}ensureDirectory(){try{d.existsSync(this.dataDir)||d.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t){return E.join(this.dataDir,`${t}${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(r=>{e=r;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async read(t){if(this.cache.has(t))return this.cache.get(t);let e=this.getFilePath(t);try{if(!d.existsSync(e))return null;let i=await d.promises.readFile(e,"utf-8"),r=JSON.parse(i);return this.cache.set(t,r),r}catch(i){throw new f(`Failed to read collection "${t}"`,i)}}async write(t,e){let i=this.getFilePath(t),r=`${i}.tmp.${Date.now()}`,n=await this.acquireLock(t);try{e.updatedAt=new Date().toISOString();let s=this.prettyPrint?JSON.stringify(e,null,2):JSON.stringify(e);await d.promises.writeFile(r,s,"utf-8"),await d.promises.rename(r,i),this.cache.set(t,e);}catch(s){try{d.existsSync(r)&&await d.promises.unlink(r);}catch{}throw new f(`Failed to write collection "${t}"`,s)}finally{n();}}async exists(t){let e=this.getFilePath(t);return d.existsSync(e)}async delete(t){let e=this.getFilePath(t),i=await this.acquireLock(t);try{d.existsSync(e)&&await d.promises.unlink(e),this.cache.delete(t);}catch(r){throw new f(`Failed to delete collection "${t}"`,r)}finally{i();}}async list(){try{return (await d.promises.readdir(this.dataDir)).filter(e=>e.endsWith(this.fileExtension)).map(e=>e.slice(0,-this.fileExtension.length))}catch(t){throw new f("Failed to list collections",t)}}clearCache(t){t?this.cache.delete(t):this.cache.clear();}getDataDir(){return this.dataDir}};function D(a){return new ObjectId().toHexString()}function j(a){return typeof a!="string"||a.length===0||a.length>64?false:a.length===24?ObjectId.isValid(a):true}function l(a){if(a===null||typeof a!="object")return a;if(a instanceof Date)return new Date(a.getTime());if(a instanceof RegExp)return new RegExp(a.source,a.flags);if(Array.isArray(a))return a.map(e=>l(e));let t={};for(let e in a)Object.prototype.hasOwnProperty.call(a,e)&&(t[e]=l(a[e]));return t}function W(a,t){let e=t.split("."),i=a;for(let r of e){if(i==null||typeof i!="object")return;i=i[r];}return i}function q(a,t,e){let i=t.split("."),r=a;for(let n=0;n<i.length-1;n++){let s=i[n];(!(s in r)||typeof r[s]!="object"||r[s]===null)&&(r[s]={}),r=r[s];}r[i[i.length-1]]=e;}function _(a,t){let e=t.split("."),i=a;for(let n=0;n<e.length-1;n++){let s=e[n];if(!(s in i)||typeof i[s]!="object"||i[s]===null)return false;i=i[s];}let r=e[e.length-1];return r in i?(delete i[r],true):false}function F(a){return typeof a=="object"&&a!==null&&!Array.isArray(a)&&!(a instanceof Date)&&!(a instanceof RegExp)}var P=class{filter(t,e){return !e||Object.keys(e).length===0?t:t.filter(i=>this.matches(i,e))}matches(t,e){if("$and"in e&&e.$and)return e.$and.every(i=>this.matches(t,i));if("$or"in e&&e.$or)return e.$or.some(i=>this.matches(t,i));if("$not"in e&&e.$not)return !this.matches(t,e.$not);for(let[i,r]of Object.entries(e)){if(i.startsWith("$"))continue;let n=W(t,i);if(!this.matchesCondition(n,r))return false}return true}matchesCondition(t,e){if(!F(e)||!this.hasOperators(e))return this.isEqual(t,e);let i=e;if("$eq"in i&&!this.isEqual(t,i.$eq)||"$ne"in i&&this.isEqual(t,i.$ne)||"$gt"in i&&!this.compareValues(t,i.$gt,">")||"$gte"in i&&!this.compareValues(t,i.$gte,">=")||"$lt"in i&&!this.compareValues(t,i.$lt,"<")||"$lte"in i&&!this.compareValues(t,i.$lte,"<=")||"$in"in i&&Array.isArray(i.$in)&&!i.$in.some(r=>this.isEqual(t,r))||"$nin"in i&&Array.isArray(i.$nin)&&i.$nin.some(r=>this.isEqual(t,r)))return false;if("$exists"in i){let r=t!=null;if(i.$exists!==r)return false}return !("$regex"in i&&(typeof t!="string"||!(i.$regex instanceof RegExp?i.$regex:new RegExp(i.$regex)).test(t))||"$startsWith"in i&&(typeof t!="string"||typeof i.$startsWith!="string"||!t.startsWith(i.$startsWith))||"$endsWith"in i&&(typeof t!="string"||typeof i.$endsWith!="string"||!t.endsWith(i.$endsWith))||"$contains"in i&&(!Array.isArray(t)||!t.some(r=>this.isEqual(r,i.$contains))))}hasOperators(t){return Object.keys(t).some(e=>e.startsWith("$"))}isEqual(t,e){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(typeof t!=typeof e)return false;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)&&Array.isArray(e))return t.length!==e.length?false:t.every((i,r)=>this.isEqual(i,e[r]));if(typeof t=="object"&&typeof e=="object"){let i=Object.keys(t),r=Object.keys(e);return i.length!==r.length?false:i.every(n=>this.isEqual(t[n],e[n]))}return false}compareValues(t,e,i){if(typeof t=="number"&&typeof e=="number")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(typeof t=="string"&&typeof e=="string")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(t instanceof Date&&e instanceof Date)switch(i){case ">":return t.getTime()>e.getTime();case ">=":return t.getTime()>=e.getTime();case "<":return t.getTime()<e.getTime();case "<=":return t.getTime()<=e.getTime()}return false}};var C=class{name;storage;queryEngine;autoSave;saveDebounce;idGenerator;schema;saveTimeout=null;pendingSave=null;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new P,this.autoSave=i.autoSave??true,this.saveDebounce=i.saveDebounce??0,this.idGenerator=i.idGenerator??D,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new T(this.name,e.error.issues);return e.data}async getData(){let t=await this.storage.read(this.name);if(!t){let e={name:this.name,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};return await this.storage.write(this.name,e),e}return t}async save(t){if(this.autoSave){if(this.saveDebounce>0)return this.saveTimeout&&clearTimeout(this.saveTimeout),new Promise(e=>{this.saveTimeout=setTimeout(async()=>{await this.storage.write(this.name,t),this.saveTimeout=null,e();},this.saveDebounce);});await this.storage.write(this.name,t);}}async flush(){this.saveTimeout&&(clearTimeout(this.saveTimeout),this.saveTimeout=null),this.pendingSave&&await this.pendingSave;let t=await this.getData();await this.storage.write(this.name,t);}async insert(t){let e=await this.getData(),i=t._id||this.idGenerator();if(e.documents.some(s=>s._id===i))throw new w(this.name,i);let r={...l(t),_id:i},n=this.validate(r);return e.documents.push(n),await this.save(e),l(n)}async insertFast(t){let e=await this.getData(),i=t._id||this.idGenerator(),r={...l(t),_id:i},n=this.validate(r);return e.documents.push(n),await this.save(e),l(n)}async insertMany(t){if(t.length===0)return [];let e=await this.getData(),i=[],r=new Set(e.documents.map(n=>n._id));for(let n of t){let s=n._id||this.idGenerator();if(r.has(s))throw new w(this.name,s);r.add(s);let o={...l(n),_id:s},c=this.validate(o);e.documents.push(c),i.push(l(c));}return await this.save(e),i}async find(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);return e?.sort&&(r=this.sortDocuments(r,e.sort)),e?.skip&&e.skip>0&&(r=r.slice(e.skip)),e?.limit&&e.limit>0&&(r=r.slice(0,e.limit)),r.map(n=>l(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){return this.findOne({_id:t})}async count(t){let e=await this.getData();return this.queryEngine.filter(e.documents,t).length}async update(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);if(r.length===0)return 0;for(let n of r)this.applyUpdate(n,e);return await this.save(i),r.length}async updateOne(t,e){let i=await this.getData(),r=this.queryEngine.filter(i.documents,t);if(r.length===0)return null;let n=r[0];return this.applyUpdate(n,e),await this.save(i),l(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.getData(),i=e.documents.length;e.documents=e.documents.filter(n=>!this.queryEngine.matches(n,t));let r=i-e.documents.length;return r>0&&await this.save(e),r}async deleteOne(t){let e=await this.getData(),i=e.documents.findIndex(n=>this.queryEngine.matches(n,t));if(i===-1)return null;let[r]=e.documents.splice(i,1);return await this.save(e),l(r)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){let t=await this.getData();t.documents=[],await this.save(t);}async drop(){await this.storage.delete(this.name);}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let r=e;if(r.$set)for(let[n,s]of Object.entries(r.$set))n!=="_id"&&q(t,n,s);if(r.$unset)for(let n of Object.keys(r.$unset))n!=="_id"&&_(t,n);if(r.$inc)for(let[n,s]of Object.entries(r.$inc)){let o=t[n];typeof o=="number"&&typeof s=="number"&&(t[n]=o+s);}if(r.$push)for(let[n,s]of Object.entries(r.$push)){let o=t[n];Array.isArray(o)&&o.push(s);}if(r.$pull)for(let[n,s]of Object.entries(r.$pull)){let o=t[n];if(Array.isArray(o)){let c=o.findIndex(u=>JSON.stringify(u)===JSON.stringify(s));c!==-1&&o.splice(c,1);}}if(r.$addToSet)for(let[n,s]of Object.entries(r.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(u=>JSON.stringify(u)===JSON.stringify(s))||o.push(s));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((r,n)=>{for(let[s,o]of i){let c=r[s],u=n[s],h=0;if(c===u?h=0:c==null?h=1:u==null?h=-1:typeof c=="number"&&typeof u=="number"?h=c-u:typeof c=="string"&&typeof u=="string"?h=c.localeCompare(u):c instanceof Date&&u instanceof Date?h=c.getTime()-u.getTime():h=String(c).localeCompare(String(u)),h!==0)return h*(o===-1||o==="desc"?-1:1)}return 0})}};var x=class{batchSize;flushInterval;coalesceWrites;batchProcessor;queue=new Map;flushTimer=null;pendingFlush=null;totalQueued=0;isShuttingDown=false;constructor(t,e={}){this.batchSize=e.batchSize??1e3,this.flushInterval=e.flushInterval??100,this.coalesceWrites=e.coalesceWrites??true,this.batchProcessor=t;}async enqueue(t){if(this.isShuttingDown)throw new Error("WriteQueue is shutting down, no new writes accepted");return new Promise((e,i)=>{let r={operation:t,resolve:e,reject:i,timestamp:Date.now()},n=t.collectionName,s=this.queue.get(n);s||(s=[],this.queue.set(n,s)),!(this.coalesceWrites&&this.tryCoalesce(n,r))&&(s.push(r),this.totalQueued++,this.scheduleFlush(),this.totalQueued>=this.batchSize&&this.flush().catch(i));})}tryCoalesce(t,e){let i=this.queue.get(t);if(!i||i.length===0)return false;let r=e.operation;if(r.type==="update")for(let n=i.length-1;n>=0;n--){let s=i[n].operation;if(s.type==="update"&&s.documentId===r.documentId){s.changes={...s.changes,...r.changes};let o=i[n].resolve;return i[n].resolve=()=>{o(),e.resolve();},true}}if(r.type==="delete")for(let n=i.length-1;n>=0;n--){let s=i[n].operation;(s.type==="insert"&&s.document._id===r.documentId||s.type==="update"&&s.documentId===r.documentId)&&(i.splice(n,1),this.totalQueued--);}if(r.type==="clear"||r.type==="fullWrite"){for(let n of i)n.resolve();i.length=0,this.totalQueued-=i.length;}return false}scheduleFlush(){this.flushTimer===null&&(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush().catch(t=>{console.error("WriteQueue flush error:",t);});},this.flushInterval));}async flush(){if(this.pendingFlush)return this.pendingFlush;if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.totalQueued===0)return;let t=this.queue;this.queue=new Map,this.totalQueued=0;let e=new Map;for(let[i,r]of t)e.set(i,r.map(n=>n.operation));this.pendingFlush=this.processBatch(t,e),await this.pendingFlush,this.pendingFlush=null;}async processBatch(t,e){try{await this.batchProcessor(e);for(let i of t.values())for(let r of i)r.resolve();}catch(i){for(let r of t.values())for(let n of r)n.reject(i);}}async shutdown(){this.isShuttingDown=true,await this.flush();}pending(){return this.totalQueued}isEmpty(){return this.totalQueued===0}isClosing(){return this.isShuttingDown}};var S=class{dataDir;partitionCount;prettyPrint;fileExtension;cache=new Map;locks=new Map;constructor(t,e={}){this.dataDir=E.resolve(t),this.partitionCount=e.partitionCount??16,this.prettyPrint=e.prettyPrint??true,this.fileExtension=e.fileExtension??".json",this.ensureDirectory();}ensureDirectory(){try{d.existsSync(this.dataDir)||d.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getPartitionIndex(t){let e=0;for(let i=0;i<t.length;i++){let r=t.charCodeAt(i);e=(e<<5)-e+r,e=e&e;}return Math.abs(e)%this.partitionCount}getPartitionFileName(t,e){return `${t}_p${e.toString().padStart(3,"0")}${this.fileExtension}`}getPartitionFilePath(t,e){return E.join(this.dataDir,this.getPartitionFileName(t,e))}getCacheKey(t,e){return `${t}:${e}`}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(r=>{e=r;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async readPartition(t,e){let i=this.getCacheKey(t,e);if(this.cache.has(i))return this.cache.get(i);let r=this.getPartitionFilePath(t,e);try{if(!d.existsSync(r))return null;let n=await d.promises.readFile(r,"utf-8"),s=JSON.parse(n);return this.cache.set(i,s),s}catch(n){throw new f(`Failed to read partition ${e} for collection "${t}"`,n)}}async writePartition(t,e,i){let r=this.getPartitionFilePath(t,e),n=`${r}.tmp.${Date.now()}`,s=this.getCacheKey(t,e),o=await this.acquireLock(s);try{i.updatedAt=new Date().toISOString();let c=this.prettyPrint?JSON.stringify(i,null,2):JSON.stringify(i);await d.promises.writeFile(n,c,"utf-8"),await d.promises.rename(n,r),this.cache.set(s,i);}catch(c){try{d.existsSync(n)&&await d.promises.unlink(n);}catch{}throw new f(`Failed to write partition ${e} for collection "${t}"`,c)}finally{o();}}async readAllPartitions(t){let e=new Map,i=[];for(let r=0;r<this.partitionCount;r++)i.push(this.readPartition(t,r).then(n=>{n&&e.set(r,n);}));return await Promise.all(i),e}async writePartitions(t,e){let i=[];for(let[r,n]of e)i.push(this.writePartition(t,r,n));await Promise.all(i);}async initializePartitions(t){let e=[];for(let i=0;i<this.partitionCount;i++)if(!d.existsSync(this.getPartitionFilePath(t,i))){let n={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};e.push(this.writePartition(t,i,n));}await Promise.all(e);}async getPartitionInfo(t){let e=await this.readAllPartitions(t),i=[];for(let r=0;r<this.partitionCount;r++){let n=e.get(r);i.push({name:this.getPartitionFileName(t,r),partitionIndex:r,documentCount:n?.documents.length??0});}return i}async getAllDocuments(t){let e=await this.readAllPartitions(t),i=[];for(let r of e.values())i.push(...r.documents);return i}async findById(t,e){let i=this.getPartitionIndex(e),r=await this.readPartition(t,i);return r?r.documents.find(n=>n._id===e)??null:null}async deleteCollection(t){let e=[];for(let i=0;i<this.partitionCount;i++){let r=this.getPartitionFilePath(t,i),n=this.getCacheKey(t,i);e.push((async()=>{let s=await this.acquireLock(n);try{d.existsSync(r)&&await d.promises.unlink(r),this.cache.delete(n);}finally{s();}})());}await Promise.all(e);}clearCache(t){if(t)for(let e=0;e<this.partitionCount;e++)this.cache.delete(this.getCacheKey(t,e));else this.cache.clear();}getPartitionCount(){return this.partitionCount}async listCollections(){try{let t=await d.promises.readdir(this.dataDir),e=new Set,i=new RegExp(`^(.+)_p\\d{3}\\${this.fileExtension}$`);for(let r of t){let n=r.match(i);n&&e.add(n[1]);}return Array.from(e)}catch(t){throw new f("Failed to list partitioned collections",t)}}};var k=class{maxConcurrent;maxQueueSize;queue=[];activeCount=0;completedCount=0;failedCount=0;isShuttingDown=false;constructor(t={}){this.maxConcurrent=t.maxConcurrent??4,this.maxQueueSize=t.maxQueueSize??1e4;}async submit(t,e=0){if(this.isShuttingDown)throw new Error("WorkerPool is shutting down, no new tasks accepted");if(this.queue.length>=this.maxQueueSize)throw new Error("WorkerPool queue is full, task rejected (backpressure)");return new Promise((i,r)=>{let n={task:t,resolve:i,reject:r,priority:e},s=false;for(let o=0;o<this.queue.length;o++)if(e>this.queue[o].priority){this.queue.splice(o,0,n),s=true;break}s||this.queue.push(n),this.processNext();})}async submitAll(t,e=0){let i=t.map(r=>this.submit(r,e));return Promise.all(i)}async*submitStream(t,e=0){let i=t.map(r=>this.submit(r,e));for(let r of i)yield await r;}processNext(){if(this.activeCount>=this.maxConcurrent||this.queue.length===0)return;let t=this.queue.shift();t&&(this.activeCount++,t.task().then(e=>{this.completedCount++,t.resolve(e);}).catch(e=>{this.failedCount++,t.reject(e);}).finally(()=>{this.activeCount--,this.processNext();}));}async drain(){return new Promise(t=>{let e=()=>{this.activeCount===0&&this.queue.length===0?t():setImmediate(e);};e();})}async shutdown(){this.isShuttingDown=true,await this.drain();}getStats(){return {activeWorkers:this.activeCount,queuedTasks:this.queue.length,completedTasks:this.completedCount,failedTasks:this.failedCount}}isIdle(){return this.activeCount===0&&this.queue.length===0}isClosing(){return this.isShuttingDown}queueSize(){return this.queue.length}activeWorkers(){return this.activeCount}};async function Q(a,t,e){let i=[],r=0;async function n(){let o=r++;o>=a.length||(i[o]=await e(a[o],o),await n());}let s=Array(Math.min(t,a.length)).fill(null).map(()=>n());return await Promise.all(s),i}var V={partitions:16,batchSize:1e3,flushInterval:100,maxConcurrentIO:4,coalesceWrites:true},O=class{partitionManager;writeQueue;workerPool;options;constructor(t){let e=t.highConcurrency;this.options={...V,...e},this.partitionManager=new S(t.dataDir,{partitionCount:this.options.partitions,prettyPrint:t.prettyPrint??true,fileExtension:t.fileExtension??".json"}),this.workerPool=new k({maxConcurrent:this.options.maxConcurrentIO});let i=this.processBatch.bind(this);this.writeQueue=new x(i,{batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,coalesceWrites:this.options.coalesceWrites});}async processBatch(t){let e=Array.from(t.entries());await Q(e,this.options.maxConcurrentIO,async([i,r])=>{await this.processCollectionOperations(i,r);});}async processCollectionOperations(t,e){let i=new Map,r=n=>{let s=i.get(n);return s||(s=[],i.set(n,s)),s};for(let n of e)if(n.type==="fullWrite"||n.type==="clear")for(let s=0;s<this.partitionManager.getPartitionCount();s++)r(s).push(n);else if(n.type==="insert"){let s=this.partitionManager.getPartitionIndex(n.document._id);r(s).push(n);}else if(n.type==="update"||n.type==="delete"){let s=this.partitionManager.getPartitionIndex(n.documentId);r(s).push(n);}else if(n.type==="bulkInsert")for(let s of n.documents){let o=this.partitionManager.getPartitionIndex(s._id);r(o).push({type:"insert",collectionName:t,document:s});}else if(n.type==="bulkUpdate"||n.type==="bulkDelete")for(let s of n.documentIds){let o=this.partitionManager.getPartitionIndex(s);n.type==="bulkUpdate"?r(o).push({type:"update",collectionName:t,documentId:s,changes:n.changes}):r(o).push({type:"delete",collectionName:t,documentId:s});}await Q(Array.from(i.entries()),this.options.maxConcurrentIO,async([n,s])=>{await this.processPartitionOperations(t,n,s);});}async processPartitionOperations(t,e,i){let r=await this.partitionManager.readPartition(t,e);r||(r={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});for(let n of i)switch(n.type){case "insert":r.documents.push(n.document);break;case "update":{let s=r.documents.findIndex(o=>o._id===n.documentId);s!==-1&&Object.assign(r.documents[s],n.changes);break}case "delete":{let s=r.documents.findIndex(o=>o._id===n.documentId);s!==-1&&r.documents.splice(s,1);break}case "clear":r.documents=[];break;case "fullWrite":{let s=n.data.documents.filter(o=>this.partitionManager.getPartitionIndex(o._id)===e);r.documents=s;break}}await this.partitionManager.writePartition(t,e,r);}async insert(t,e){await this.writeQueue.enqueue({type:"insert",collectionName:t,document:e});}async insertMany(t,e){await this.writeQueue.enqueue({type:"bulkInsert",collectionName:t,documents:e});}async update(t,e,i){await this.writeQueue.enqueue({type:"update",collectionName:t,documentId:e,changes:i});}async delete(t,e){await this.writeQueue.enqueue({type:"delete",collectionName:t,documentId:e});}async clear(t){await this.writeQueue.enqueue({type:"clear",collectionName:t});}async findById(t,e){return await this.writeQueue.flush(),this.partitionManager.findById(t,e)}async readAll(t){return await this.writeQueue.flush(),this.partitionManager.getAllDocuments(t)}async exists(t){return (await this.partitionManager.listCollections()).includes(t)}async initializeCollection(t){await this.partitionManager.initializePartitions(t);}async deleteCollection(t){await this.writeQueue.flush(),await this.partitionManager.deleteCollection(t);}async listCollections(){return this.partitionManager.listCollections()}async flush(){await this.writeQueue.flush();}async shutdown(){await this.writeQueue.shutdown(),await this.workerPool.shutdown();}pendingWrites(){return this.writeQueue.pending()}getStats(){return {pendingWrites:this.writeQueue.pending(),workerPool:this.workerPool.getStats(),partitions:this.options.partitions}}clearCache(t){this.partitionManager.clearCache(t);}};var b=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new P,this.idGenerator=i.idGenerator??D,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new T(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(await this.storage.findById(this.name,e))throw new w(this.name,e);let r={...l(t),_id:e},n=this.validate(r);return await this.storage.insert(this.name,n),n}async insertFast(t){let e=t._id||this.idGenerator(),i={...l(t),_id:e},r=this.validate(i);return await this.storage.insert(this.name,r),r}async insertMany(t){if(t.length===0)return [];let e=[],i=[];for(let r of t){let n=r._id||this.idGenerator(),s={...l(r),_id:n},o=this.validate(s);i.push(o),e.push(l(o));}return await this.storage.insertMany(this.name,i),e}async find(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);return e?.sort&&(r=this.sortDocuments(r,e.sort)),e?.skip&&e.skip>0&&(r=r.slice(e.skip)),e?.limit&&e.limit>0&&(r=r.slice(0,e.limit)),r.map(n=>l(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.findById(this.name,t);return e?l(e):null}async count(t){let e=await this.storage.readAll(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);if(r.length===0)return 0;for(let n of r){let s=this.getUpdateChanges(n,e);await this.storage.update(this.name,n._id,s);}return r.length}async updateOne(t,e){let i=await this.storage.readAll(this.name),r=this.queryEngine.filter(i,t);if(r.length===0)return null;let n=r[0],s=this.getUpdateChanges(n,e);return await this.storage.update(this.name,n._id,s),Object.assign(n,s),l(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;for(let r of i)await this.storage.delete(this.name,r._id);return i.length}async deleteOne(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let r=i[0];return await this.storage.delete(this.name,r._id),l(r)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){await this.storage.clear(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}getUpdateChanges(t,e){if(!Object.keys(e).some(s=>s.startsWith("$")))return e;let r=e,n={};if(r.$set)for(let[s,o]of Object.entries(r.$set))s!=="_id"&&(n[s]=o);if(r.$inc)for(let[s,o]of Object.entries(r.$inc)){let c=t[s];typeof c=="number"&&typeof o=="number"&&(n[s]=c+o);}if(r.$push)for(let[s,o]of Object.entries(r.$push)){let c=t[s];Array.isArray(c)&&(n[s]=[...c,o]);}if(r.$pull)for(let[s,o]of Object.entries(r.$pull)){let c=t[s];Array.isArray(c)&&(n[s]=c.filter(u=>JSON.stringify(u)!==JSON.stringify(o)));}if(r.$addToSet)for(let[s,o]of Object.entries(r.$addToSet)){let c=t[s];Array.isArray(c)&&(c.some(h=>JSON.stringify(h)===JSON.stringify(o))?n[s]=c:n[s]=[...c,o]);}return n}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((r,n)=>{for(let[s,o]of i){let c=r[s],u=n[s],h=0;if(c===u?h=0:c==null?h=1:u==null?h=-1:typeof c=="number"&&typeof u=="number"?h=c-u:typeof c=="string"&&typeof u=="string"?h=c.localeCompare(u):c instanceof Date&&u instanceof Date?h=c.getTime()-u.getTime():h=String(c).localeCompare(String(u)),h!==0)return h*(o===-1||o==="desc"?-1:1)}return 0})}};var B={autoSave:true,saveDebounce:0,prettyPrint:true,fileExtension:".json"},M=class{options;storage;hcStorage;collections=new Map;isHighConcurrency;connected=false;constructor(t){this.options={...B,...t},this.isHighConcurrency=t.highConcurrency?.enabled??false,this.isHighConcurrency?(this.storage=null,this.hcStorage=new O(this.options)):(this.storage=new $(this.options),this.hcStorage=null);}getStorage(){if(this.storage===null)throw new Error("Storage is not available in high-concurrency mode");return this.storage}getHCStorage(){if(this.hcStorage===null)throw new Error("HighConcurrencyStorage is not available in standard mode");return this.hcStorage}async connect(){if(!this.connected){if(this.isHighConcurrency){let t=await this.getHCStorage().listCollections();for(let e of t)this.getOrCreateCollection(e);}else {let t=await this.getStorage().list();for(let e of t)this.getOrCreateCollection(e);}this.connected=true;}}async close(){if(this.connected){for(let t of this.collections.values())await t.flush();this.isHighConcurrency&&this.hcStorage&&await this.hcStorage.shutdown(),this.collections.clear(),this.storage&&this.storage.clearCache(),this.hcStorage&&this.hcStorage.clearCache(),this.connected=false;}}collection(t,e){if(!t||typeof t!="string")throw new v("Collection name must be a non-empty string");if(!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(t))throw new v("Collection name must start with a letter or underscore and contain only letters, numbers, underscores, and hyphens");return this.getOrCreateCollection(t,e)}getOrCreateCollection(t,e){if(this.collections.has(t))return this.collections.get(t);if(this.isHighConcurrency){let i=new b(t,this.getHCStorage(),{schema:e?.schema});this.collections.set(t,i);}else {let i=new C(t,this.getStorage(),{autoSave:this.options.autoSave,saveDebounce:this.options.saveDebounce,schema:e?.schema});this.collections.set(t,i);}return this.collections.get(t)}async hasCollection(t){return this.isHighConcurrency?this.getHCStorage().exists(t):this.getStorage().exists(t)}async listCollections(){return this.isHighConcurrency?this.getHCStorage().listCollections():this.getStorage().list()}async dropCollection(t){let e=this.collections.get(t);e?(await e.drop(),this.collections.delete(t)):this.isHighConcurrency?await this.getHCStorage().deleteCollection(t):await this.getStorage().delete(t);}async drop(){if(this.isHighConcurrency){let t=this.getHCStorage(),e=await t.listCollections();for(let i of e)await t.deleteCollection(i);}else {let t=this.getStorage(),e=await t.list();for(let i of e)await t.delete(i);}this.collections.clear(),this.storage?.clearCache(),this.hcStorage?.clearCache();}getDataDir(){return this.isHighConcurrency?this.options.dataDir:this.getStorage().getDataDir()}isConnected(){return this.connected}isHighConcurrencyMode(){return this.isHighConcurrency}getStats(){return !this.isHighConcurrency||!this.hcStorage?null:this.hcStorage.getStats()}async flush(){for(let t of this.collections.values())await t.flush();}};export{C as Collection,v as CollectionError,A as DocumentNotFoundError,w as DuplicateKeyError,b as HighConcurrencyCollection,O as HighConcurrencyStorage,M as JsonDB,y as JsonDBError,S as PartitionManager,f as StorageError,T as ValidationError,k as WorkerPool,x as WriteQueue,D as generateId,j as isValidId,Q as parallelLimit};
|
|
1
|
+
import*as d from'fs';import*as x from'path';import {ObjectId}from'bson';var P=class extends Error{constructor(t){super(t),this.name="JsonDBError",Error.captureStackTrace?.(this,this.constructor);}},R=class extends P{constructor(t,e){super(e?`Document with id "${e}" not found in collection "${t}"`:`Document not found in collection "${t}"`),this.name="DocumentNotFoundError";}},w=class extends P{constructor(t,e){super(`Duplicate key error: document with id "${e}" already exists in collection "${t}"`),this.name="DuplicateKeyError";}},T=class extends P{collectionName;issues;field;value;constructor(t,e,i,s){let n=e.map(r=>`${r.path.join(".")}: ${r.message}`).join("; ");super(`Validation failed for collection "${t}": ${n}`),this.name="ValidationError",this.collectionName=t,this.issues=e,this.field=i,this.value=s;}},f=class extends P{cause;constructor(t,e){super(t),this.name="StorageError",this.cause=e;}},C=class extends P{constructor(t){super(t),this.name="CollectionError";}};var z=class{dataDir;fileExtension;prettyPrint;cache=new Map;locks=new Map;constructor(t){this.dataDir=x.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.ensureDirectory();}ensureDirectory(){try{d.existsSync(this.dataDir)||d.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t){return x.join(this.dataDir,`${t}${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async read(t){if(this.cache.has(t))return this.cache.get(t);let e=this.getFilePath(t);try{if(!d.existsSync(e))return null;let i=await d.promises.readFile(e,"utf-8"),s=JSON.parse(i);return this.cache.set(t,s),s}catch(i){throw new f(`Failed to read collection "${t}"`,i)}}async write(t,e){let i=this.getFilePath(t),s=`${i}.tmp.${Date.now()}`,n=await this.acquireLock(t);try{e.updatedAt=new Date().toISOString();let r=this.prettyPrint?JSON.stringify(e,null,2):JSON.stringify(e);await d.promises.writeFile(s,r,"utf-8"),await d.promises.rename(s,i),this.cache.set(t,e);}catch(r){try{d.existsSync(s)&&await d.promises.unlink(s);}catch{}throw new f(`Failed to write collection "${t}"`,r)}finally{n();}}async exists(t){let e=this.getFilePath(t);return d.existsSync(e)}async delete(t){let e=this.getFilePath(t),i=await this.acquireLock(t);try{d.existsSync(e)&&await d.promises.unlink(e),this.cache.delete(t);}catch(s){throw new f(`Failed to delete collection "${t}"`,s)}finally{i();}}async list(){try{return (await d.promises.readdir(this.dataDir)).filter(e=>e.endsWith(this.fileExtension)).map(e=>e.slice(0,-this.fileExtension.length))}catch(t){throw new f("Failed to list collections",t)}}clearCache(t){t?this.cache.delete(t):this.cache.clear();}getDataDir(){return this.dataDir}};function v(c){return new ObjectId().toHexString()}function U(c){return typeof c!="string"||c.length===0||c.length>64?false:c.length===24?ObjectId.isValid(c):true}function h(c){if(c===null||typeof c!="object")return c;if(c instanceof Date)return new Date(c.getTime());if(c instanceof RegExp)return new RegExp(c.source,c.flags);if(Array.isArray(c))return c.map(e=>h(e));let t={};for(let e in c)Object.prototype.hasOwnProperty.call(c,e)&&(t[e]=h(c[e]));return t}function V(c,t){let e=t.split("."),i=c;for(let s of e){if(i==null||typeof i!="object")return;i=i[s];}return i}function k(c,t,e){let i=t.split("."),s=c;for(let n=0;n<i.length-1;n++){let r=i[n];(!(r in s)||typeof s[r]!="object"||s[r]===null)&&(s[r]={}),s=s[r];}s[i[i.length-1]]=e;}function b(c,t){let e=t.split("."),i=c;for(let n=0;n<e.length-1;n++){let r=e[n];if(!(r in i)||typeof i[r]!="object"||i[r]===null)return false;i=i[r];}let s=e[e.length-1];return s in i?(delete i[s],true):false}function H(c){return typeof c=="object"&&c!==null&&!Array.isArray(c)&&!(c instanceof Date)&&!(c instanceof RegExp)}function O(c,t){if(!t||Object.keys(t).length===0)return c;let e=Object.entries(t).filter(([a])=>a!=="_id"),i=t._id,s=e.some(([,a])=>a===1||a===true),n=e.some(([,a])=>a===0||a===false);if(s&&n)throw new Error("Cannot mix inclusion and exclusion in projection");let r=s,o={};if(r){i!==0&&i!==false&&"_id"in c&&(o._id=c._id);for(let[l,u]of e)(u===1||u===true)&&l in c&&(o[l]=c[l]);}else {let a=new Set(e.filter(([,u])=>u===0||u===false).map(([u])=>u));(i===0||i===false)&&a.add("_id");for(let u of Object.keys(c))a.has(u)||(o[u]=c[u]);}return o}var D=class{filter(t,e){return !e||Object.keys(e).length===0?t:t.filter(i=>this.matches(i,e))}matches(t,e){if("$and"in e&&e.$and)return e.$and.every(i=>this.matches(t,i));if("$or"in e&&e.$or)return e.$or.some(i=>this.matches(t,i));if("$not"in e&&e.$not)return !this.matches(t,e.$not);for(let[i,s]of Object.entries(e)){if(i.startsWith("$"))continue;let n=V(t,i);if(!this.matchesCondition(n,s))return false}return true}matchesCondition(t,e){if(!H(e)||!this.hasOperators(e))return this.isEqual(t,e);let i=e;if("$eq"in i&&!this.isEqual(t,i.$eq)||"$ne"in i&&this.isEqual(t,i.$ne)||"$gt"in i&&!this.compareValues(t,i.$gt,">")||"$gte"in i&&!this.compareValues(t,i.$gte,">=")||"$lt"in i&&!this.compareValues(t,i.$lt,"<")||"$lte"in i&&!this.compareValues(t,i.$lte,"<=")||"$in"in i&&Array.isArray(i.$in)&&!i.$in.some(s=>this.isEqual(t,s))||"$nin"in i&&Array.isArray(i.$nin)&&i.$nin.some(s=>this.isEqual(t,s)))return false;if("$exists"in i){let s=t!=null;if(i.$exists!==s)return false}if("$regex"in i&&(typeof t!="string"||!(i.$regex instanceof RegExp?i.$regex:new RegExp(i.$regex)).test(t))||"$startsWith"in i&&(typeof t!="string"||typeof i.$startsWith!="string"||!t.startsWith(i.$startsWith))||"$endsWith"in i&&(typeof t!="string"||typeof i.$endsWith!="string"||!t.endsWith(i.$endsWith))||"$contains"in i&&(!Array.isArray(t)||!t.some(s=>this.isEqual(s,i.$contains)))||"$all"in i&&Array.isArray(i.$all)&&(!Array.isArray(t)||!i.$all.every(s=>t.some(n=>this.isEqual(n,s)))))return false;if("$elemMatch"in i&&i.$elemMatch){if(!Array.isArray(t))return false;let s=i.$elemMatch;if(!t.some(r=>typeof r!="object"||r===null?false:Object.entries(s).every(([o,a])=>{let l=r[o];return this.matchesCondition(l,a)})))return false}if("$size"in i&&typeof i.$size=="number"&&(!Array.isArray(t)||t.length!==i.$size))return false;if("$type"in i&&i.$type){let s=i.$type,n;if(t===null?n="null":t===void 0?n="undefined":Array.isArray(t)?n="array":n=typeof t,n!==s)return false}if("$mod"in i&&Array.isArray(i.$mod)&&i.$mod.length===2){if(typeof t!="number")return false;let[s,n]=i.$mod;if(t%s!==n)return false}return true}hasOperators(t){return Object.keys(t).some(e=>e.startsWith("$"))}isEqual(t,e){if(t===e)return true;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(typeof t!=typeof e)return false;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(Array.isArray(t)&&Array.isArray(e))return t.length!==e.length?false:t.every((i,s)=>this.isEqual(i,e[s]));if(typeof t=="object"&&typeof e=="object"){let i=Object.keys(t),s=Object.keys(e);return i.length!==s.length?false:i.every(n=>this.isEqual(t[n],e[n]))}return false}compareValues(t,e,i){if(typeof t=="number"&&typeof e=="number")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(typeof t=="string"&&typeof e=="string")switch(i){case ">":return t>e;case ">=":return t>=e;case "<":return t<e;case "<=":return t<=e}if(t instanceof Date&&e instanceof Date)switch(i){case ">":return t.getTime()>e.getTime();case ">=":return t.getTime()>=e.getTime();case "<":return t.getTime()<e.getTime();case "<=":return t.getTime()<=e.getTime()}return false}};var $=class{name;storage;queryEngine;autoSave;saveDebounce;idGenerator;schema;saveTimeout=null;pendingSave=null;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new D,this.autoSave=i.autoSave??true,this.saveDebounce=i.saveDebounce??0,this.idGenerator=i.idGenerator??v,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new T(this.name,e.error.issues);return e.data}async getData(){let t=await this.storage.read(this.name);if(!t){let e={name:this.name,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};return await this.storage.write(this.name,e),e}return t}async save(t){if(this.autoSave){if(this.saveDebounce>0)return this.saveTimeout&&clearTimeout(this.saveTimeout),new Promise(e=>{this.saveTimeout=setTimeout(async()=>{await this.storage.write(this.name,t),this.saveTimeout=null,e();},this.saveDebounce);});await this.storage.write(this.name,t);}}async flush(){this.saveTimeout&&(clearTimeout(this.saveTimeout),this.saveTimeout=null),this.pendingSave&&await this.pendingSave;let t=await this.getData();await this.storage.write(this.name,t);}async insert(t){let e=await this.getData(),i=t._id||this.idGenerator();if(e.documents.some(r=>r._id===i))throw new w(this.name,i);let s={...h(t),_id:i},n=this.validate(s);return e.documents.push(n),await this.save(e),h(n)}async insertFast(t){let e=await this.getData(),i=t._id||this.idGenerator(),s={...h(t),_id:i},n=this.validate(s);return e.documents.push(n),await this.save(e),h(n)}async insertMany(t){if(t.length===0)return [];let e=await this.getData(),i=[],s=new Set(e.documents.map(n=>n._id));for(let n of t){let r=n._id||this.idGenerator();if(s.has(r))throw new w(this.name,r);s.add(r);let o={...h(n),_id:r},a=this.validate(o);e.documents.push(a),i.push(h(a));}return await this.save(e),i}async find(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),e?.projection?s.map(n=>O(h(n),e.projection)):s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){return this.findOne({_id:t})}async count(t){let e=await this.getData();return this.queryEngine.filter(e.documents,t).length}async update(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);if(s.length===0)return 0;for(let n of s)this.applyUpdate(n,e);return await this.save(i),s.length}async updateOne(t,e){let i=await this.getData(),s=this.queryEngine.filter(i.documents,t);if(s.length===0)return null;let n=s[0];return this.applyUpdate(n,e),await this.save(i),h(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.getData(),i=e.documents.length;e.documents=e.documents.filter(n=>!this.queryEngine.matches(n,t));let s=i-e.documents.length;return s>0&&await this.save(e),s}async deleteOne(t){let e=await this.getData(),i=e.documents.findIndex(n=>this.queryEngine.matches(n,t));if(i===-1)return null;let[s]=e.documents.splice(i,1);return await this.save(e),h(s)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){let t=await this.getData();t.documents=[],await this.save(t);}async drop(){await this.storage.delete(this.name);}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let s=e;if(s.$set)for(let[n,r]of Object.entries(s.$set))n!=="_id"&&k(t,n,r);if(s.$unset)for(let n of Object.keys(s.$unset))n!=="_id"&&b(t,n);if(s.$inc)for(let[n,r]of Object.entries(s.$inc)){let o=t[n];typeof o=="number"&&typeof r=="number"&&(t[n]=o+r);}if(s.$push)for(let[n,r]of Object.entries(s.$push)){let o=t[n];Array.isArray(o)&&o.push(r);}if(s.$pull)for(let[n,r]of Object.entries(s.$pull)){let o=t[n];if(Array.isArray(o)){let a=o.findIndex(l=>JSON.stringify(l)===JSON.stringify(r));a!==-1&&o.splice(a,1);}}if(s.$addToSet)for(let[n,r]of Object.entries(s.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(l=>JSON.stringify(l)===JSON.stringify(r))||o.push(r));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var A=class{batchSize;flushInterval;coalesceWrites;batchProcessor;queue=new Map;flushTimer=null;pendingFlush=null;totalQueued=0;isShuttingDown=false;constructor(t,e={}){this.batchSize=e.batchSize??1e3,this.flushInterval=e.flushInterval??100,this.coalesceWrites=e.coalesceWrites??true,this.batchProcessor=t;}async enqueue(t){if(this.isShuttingDown)throw new Error("WriteQueue is shutting down, no new writes accepted");return new Promise((e,i)=>{let s={operation:t,resolve:e,reject:i,timestamp:Date.now()},n=t.collectionName,r=this.queue.get(n);r||(r=[],this.queue.set(n,r)),!(this.coalesceWrites&&this.tryCoalesce(n,s))&&(r.push(s),this.totalQueued++,this.scheduleFlush(),this.totalQueued>=this.batchSize&&this.flush().catch(i));})}tryCoalesce(t,e){let i=this.queue.get(t);if(!i||i.length===0)return false;let s=e.operation;if(s.type==="update")for(let n=i.length-1;n>=0;n--){let r=i[n].operation;if(r.type==="update"&&r.documentId===s.documentId){r.changes={...r.changes,...s.changes};let o=i[n].resolve;return i[n].resolve=()=>{o(),e.resolve();},true}}if(s.type==="delete")for(let n=i.length-1;n>=0;n--){let r=i[n].operation;(r.type==="insert"&&r.document._id===s.documentId||r.type==="update"&&r.documentId===s.documentId)&&(i.splice(n,1),this.totalQueued--);}if(s.type==="clear"||s.type==="fullWrite"){for(let n of i)n.resolve();i.length=0,this.totalQueued-=i.length;}return false}scheduleFlush(){this.flushTimer===null&&(this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flush().catch(t=>{console.error("WriteQueue flush error:",t);});},this.flushInterval));}async flush(){if(this.pendingFlush)return this.pendingFlush;if(this.flushTimer!==null&&(clearTimeout(this.flushTimer),this.flushTimer=null),this.totalQueued===0)return;let t=this.queue;this.queue=new Map,this.totalQueued=0;let e=new Map;for(let[i,s]of t)e.set(i,s.map(n=>n.operation));this.pendingFlush=this.processBatch(t,e),await this.pendingFlush,this.pendingFlush=null;}async processBatch(t,e){try{await this.batchProcessor(e);for(let i of t.values())for(let s of i)s.resolve();}catch(i){for(let s of t.values())for(let n of s)n.reject(i);}}async shutdown(){this.isShuttingDown=true,await this.flush();}pending(){return this.totalQueued}isEmpty(){return this.totalQueued===0}isClosing(){return this.isShuttingDown}};var E=class{dataDir;partitionCount;prettyPrint;fileExtension;cache=new Map;locks=new Map;constructor(t,e={}){this.dataDir=x.resolve(t),this.partitionCount=e.partitionCount??16,this.prettyPrint=e.prettyPrint??true,this.fileExtension=e.fileExtension??".json",this.ensureDirectory();}ensureDirectory(){try{d.existsSync(this.dataDir)||d.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getPartitionIndex(t){let e=0;for(let i=0;i<t.length;i++){let s=t.charCodeAt(i);e=(e<<5)-e+s,e=e&e;}return Math.abs(e)%this.partitionCount}getPartitionFileName(t,e){return `${t}_p${e.toString().padStart(3,"0")}${this.fileExtension}`}getPartitionFilePath(t,e){return x.join(this.dataDir,this.getPartitionFileName(t,e))}getCacheKey(t,e){return `${t}:${e}`}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async readPartition(t,e){let i=this.getCacheKey(t,e);if(this.cache.has(i))return this.cache.get(i);let s=this.getPartitionFilePath(t,e);try{if(!d.existsSync(s))return null;let n=await d.promises.readFile(s,"utf-8"),r=JSON.parse(n);return this.cache.set(i,r),r}catch(n){throw new f(`Failed to read partition ${e} for collection "${t}"`,n)}}async writePartition(t,e,i){let s=this.getPartitionFilePath(t,e),n=`${s}.tmp.${Date.now()}`,r=this.getCacheKey(t,e),o=await this.acquireLock(r);try{i.updatedAt=new Date().toISOString();let a=this.prettyPrint?JSON.stringify(i,null,2):JSON.stringify(i);await d.promises.writeFile(n,a,"utf-8"),await d.promises.rename(n,s),this.cache.set(r,i);}catch(a){try{d.existsSync(n)&&await d.promises.unlink(n);}catch{}throw new f(`Failed to write partition ${e} for collection "${t}"`,a)}finally{o();}}async readAllPartitions(t){let e=new Map,i=[];for(let s=0;s<this.partitionCount;s++)i.push(this.readPartition(t,s).then(n=>{n&&e.set(s,n);}));return await Promise.all(i),e}async writePartitions(t,e){let i=[];for(let[s,n]of e)i.push(this.writePartition(t,s,n));await Promise.all(i);}async initializePartitions(t){let e=[];for(let i=0;i<this.partitionCount;i++)if(!d.existsSync(this.getPartitionFilePath(t,i))){let n={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};e.push(this.writePartition(t,i,n));}await Promise.all(e);}async getPartitionInfo(t){let e=await this.readAllPartitions(t),i=[];for(let s=0;s<this.partitionCount;s++){let n=e.get(s);i.push({name:this.getPartitionFileName(t,s),partitionIndex:s,documentCount:n?.documents.length??0});}return i}async getAllDocuments(t){let e=await this.readAllPartitions(t),i=[];for(let s of e.values())i.push(...s.documents);return i}async findById(t,e){let i=this.getPartitionIndex(e),s=await this.readPartition(t,i);return s?s.documents.find(n=>n._id===e)??null:null}async deleteCollection(t){let e=[];for(let i=0;i<this.partitionCount;i++){let s=this.getPartitionFilePath(t,i),n=this.getCacheKey(t,i);e.push((async()=>{let r=await this.acquireLock(n);try{d.existsSync(s)&&await d.promises.unlink(s),this.cache.delete(n);}finally{r();}})());}await Promise.all(e);}clearCache(t){if(t)for(let e=0;e<this.partitionCount;e++)this.cache.delete(this.getCacheKey(t,e));else this.cache.clear();}getPartitionCount(){return this.partitionCount}async listCollections(){try{let t=await d.promises.readdir(this.dataDir),e=new Set,i=new RegExp(`^(.+)_p\\d{3}\\${this.fileExtension}$`);for(let s of t){let n=s.match(i);n&&e.add(n[1]);}return Array.from(e)}catch(t){throw new f("Failed to list partitioned collections",t)}}};var I=class{maxConcurrent;maxQueueSize;queue=[];activeCount=0;completedCount=0;failedCount=0;isShuttingDown=false;constructor(t={}){this.maxConcurrent=t.maxConcurrent??4,this.maxQueueSize=t.maxQueueSize??1e4;}async submit(t,e=0){if(this.isShuttingDown)throw new Error("WorkerPool is shutting down, no new tasks accepted");if(this.queue.length>=this.maxQueueSize)throw new Error("WorkerPool queue is full, task rejected (backpressure)");return new Promise((i,s)=>{let n={task:t,resolve:i,reject:s,priority:e},r=false;for(let o=0;o<this.queue.length;o++)if(e>this.queue[o].priority){this.queue.splice(o,0,n),r=true;break}r||this.queue.push(n),this.processNext();})}async submitAll(t,e=0){let i=t.map(s=>this.submit(s,e));return Promise.all(i)}async*submitStream(t,e=0){let i=t.map(s=>this.submit(s,e));for(let s of i)yield await s;}processNext(){if(this.activeCount>=this.maxConcurrent||this.queue.length===0)return;let t=this.queue.shift();t&&(this.activeCount++,t.task().then(e=>{this.completedCount++,t.resolve(e);}).catch(e=>{this.failedCount++,t.reject(e);}).finally(()=>{this.activeCount--,this.processNext();}));}async drain(){return new Promise(t=>{let e=()=>{this.activeCount===0&&this.queue.length===0?t():setImmediate(e);};e();})}async shutdown(){this.isShuttingDown=true,await this.drain();}getStats(){return {activeWorkers:this.activeCount,queuedTasks:this.queue.length,completedTasks:this.completedCount,failedTasks:this.failedCount}}isIdle(){return this.activeCount===0&&this.queue.length===0}isClosing(){return this.isShuttingDown}queueSize(){return this.queue.length}activeWorkers(){return this.activeCount}};async function W(c,t,e){let i=[],s=0;async function n(){let o=s++;o>=c.length||(i[o]=await e(c[o],o),await n());}let r=Array(Math.min(t,c.length)).fill(null).map(()=>n());return await Promise.all(r),i}var K={partitions:16,batchSize:1e3,flushInterval:100,maxConcurrentIO:4,coalesceWrites:true},_=class{partitionManager;writeQueue;workerPool;options;constructor(t){let e=t.highConcurrency;this.options={...K,...e},this.partitionManager=new E(t.dataDir,{partitionCount:this.options.partitions,prettyPrint:t.prettyPrint??true,fileExtension:t.fileExtension??".json"}),this.workerPool=new I({maxConcurrent:this.options.maxConcurrentIO});let i=this.processBatch.bind(this);this.writeQueue=new A(i,{batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,coalesceWrites:this.options.coalesceWrites});}async processBatch(t){let e=Array.from(t.entries());await W(e,this.options.maxConcurrentIO,async([i,s])=>{await this.processCollectionOperations(i,s);});}async processCollectionOperations(t,e){let i=new Map,s=n=>{let r=i.get(n);return r||(r=[],i.set(n,r)),r};for(let n of e)if(n.type==="fullWrite"||n.type==="clear")for(let r=0;r<this.partitionManager.getPartitionCount();r++)s(r).push(n);else if(n.type==="insert"){let r=this.partitionManager.getPartitionIndex(n.document._id);s(r).push(n);}else if(n.type==="update"||n.type==="delete"){let r=this.partitionManager.getPartitionIndex(n.documentId);s(r).push(n);}else if(n.type==="bulkInsert")for(let r of n.documents){let o=this.partitionManager.getPartitionIndex(r._id);s(o).push({type:"insert",collectionName:t,document:r});}else if(n.type==="bulkUpdate"||n.type==="bulkDelete")for(let r of n.documentIds){let o=this.partitionManager.getPartitionIndex(r);n.type==="bulkUpdate"?s(o).push({type:"update",collectionName:t,documentId:r,changes:n.changes}):s(o).push({type:"delete",collectionName:t,documentId:r});}await W(Array.from(i.entries()),this.options.maxConcurrentIO,async([n,r])=>{await this.processPartitionOperations(t,n,r);});}async processPartitionOperations(t,e,i){let s=await this.partitionManager.readPartition(t,e);s||(s={name:t,documents:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()});for(let n of i)switch(n.type){case "insert":s.documents.push(n.document);break;case "update":{let r=s.documents.findIndex(o=>o._id===n.documentId);r!==-1&&Object.assign(s.documents[r],n.changes);break}case "delete":{let r=s.documents.findIndex(o=>o._id===n.documentId);r!==-1&&s.documents.splice(r,1);break}case "clear":s.documents=[];break;case "fullWrite":{let r=n.data.documents.filter(o=>this.partitionManager.getPartitionIndex(o._id)===e);s.documents=r;break}}await this.partitionManager.writePartition(t,e,s);}async insert(t,e){await this.writeQueue.enqueue({type:"insert",collectionName:t,document:e});}async insertMany(t,e){await this.writeQueue.enqueue({type:"bulkInsert",collectionName:t,documents:e});}async update(t,e,i){await this.writeQueue.enqueue({type:"update",collectionName:t,documentId:e,changes:i});}async delete(t,e){await this.writeQueue.enqueue({type:"delete",collectionName:t,documentId:e});}async clear(t){await this.writeQueue.enqueue({type:"clear",collectionName:t});}async findById(t,e){return await this.writeQueue.flush(),this.partitionManager.findById(t,e)}async readAll(t){return await this.writeQueue.flush(),this.partitionManager.getAllDocuments(t)}async exists(t){return (await this.partitionManager.listCollections()).includes(t)}async initializeCollection(t){await this.partitionManager.initializePartitions(t);}async deleteCollection(t){await this.writeQueue.flush(),await this.partitionManager.deleteCollection(t);}async listCollections(){return this.partitionManager.listCollections()}async flush(){await this.writeQueue.flush();}async shutdown(){await this.writeQueue.shutdown(),await this.workerPool.shutdown();}pendingWrites(){return this.writeQueue.pending()}getStats(){return {pendingWrites:this.writeQueue.pending(),workerPool:this.workerPool.getStats(),partitions:this.options.partitions}}clearCache(t){this.partitionManager.clearCache(t);}};var Q=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new D,this.idGenerator=i.idGenerator??v,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new T(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(await this.storage.findById(this.name,e))throw new w(this.name,e);let s={...h(t),_id:e},n=this.validate(s);return await this.storage.insert(this.name,n),n}async insertFast(t){let e=t._id||this.idGenerator(),i={...h(t),_id:e},s=this.validate(i);return await this.storage.insert(this.name,s),s}async insertMany(t){if(t.length===0)return [];let e=[],i=[];for(let s of t){let n=s._id||this.idGenerator(),r={...h(s),_id:n},o=this.validate(r);i.push(o),e.push(h(o));}return await this.storage.insertMany(this.name,i),e}async find(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.findById(this.name,t);return e?h(e):null}async count(t){let e=await this.storage.readAll(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return 0;for(let n of s){let r=this.getUpdateChanges(n,e);await this.storage.update(this.name,n._id,r);}return s.length}async updateOne(t,e){let i=await this.storage.readAll(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return null;let n=s[0],r=this.getUpdateChanges(n,e);return await this.storage.update(this.name,n._id,r),Object.assign(n,r),h(n)}async updateById(t,e){return this.updateOne({_id:t},e)}async delete(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;for(let s of i)await this.storage.delete(this.name,s._id);return i.length}async deleteOne(t){let e=await this.storage.readAll(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let s=i[0];return await this.storage.delete(this.name,s._id),h(s)}async deleteById(t){return this.deleteOne({_id:t})}async getAll(){return this.find()}async clear(){await this.storage.clear(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}getUpdateChanges(t,e){if(!Object.keys(e).some(r=>r.startsWith("$")))return e;let s=e,n={};if(s.$set)for(let[r,o]of Object.entries(s.$set))r!=="_id"&&(n[r]=o);if(s.$inc)for(let[r,o]of Object.entries(s.$inc)){let a=t[r];typeof a=="number"&&typeof o=="number"&&(n[r]=a+o);}if(s.$push)for(let[r,o]of Object.entries(s.$push)){let a=t[r];Array.isArray(a)&&(n[r]=[...a,o]);}if(s.$pull)for(let[r,o]of Object.entries(s.$pull)){let a=t[r];Array.isArray(a)&&(n[r]=a.filter(l=>JSON.stringify(l)!==JSON.stringify(o)));}if(s.$addToSet)for(let[r,o]of Object.entries(s.$addToSet)){let a=t[r];Array.isArray(a)&&(a.some(u=>JSON.stringify(u)===JSON.stringify(o))?n[r]=a:n[r]=[...a,o]);}return n}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var S=class{capacity;cache;constructor(t){this.capacity=t,this.cache=new Map;}get(t){if(!this.cache.has(t))return;let e=this.cache.get(t);return this.cache.delete(t),this.cache.set(t,e),e}set(t,e){if(this.cache.has(t))this.cache.delete(t);else if(this.cache.size>=this.capacity){let i=this.cache.keys().next().value;i!==void 0&&this.cache.delete(i);}this.cache.set(t,e);}has(t){return this.cache.has(t)}delete(t){return this.cache.delete(t)}clear(){this.cache.clear();}get size(){return this.cache.size}keys(){return this.cache.keys()}values(){return this.cache.values()}entries(){return this.cache.entries()}forEach(t){this.cache.forEach(t);}};var L=class{dataDir;fileExtension;prettyPrint;cacheSize;chunkSize;documentCache;collections;locks;constructor(t){this.dataDir=x.resolve(t.dataDir),this.fileExtension=t.fileExtension||".json",this.prettyPrint=t.prettyPrint??true,this.cacheSize=t.lazyLoading?.cacheSize??1e3,this.chunkSize=t.lazyLoading?.chunkSize??1e4,this.documentCache=new Map,this.collections=new Map,this.locks=new Map,this.ensureDirectory();}ensureDirectory(){try{d.existsSync(this.dataDir)||d.mkdirSync(this.dataDir,{recursive:!0});}catch(t){throw new f(`Failed to create data directory: ${this.dataDir}`,t)}}getFilePath(t,e){return e!==void 0&&this.chunkSize>0?x.join(this.dataDir,`${t}_chunk${e}${this.fileExtension}`):x.join(this.dataDir,`${t}${this.fileExtension}`)}getIndexFilePath(t){return x.join(this.dataDir,`${t}.index${this.fileExtension}`)}async acquireLock(t){for(;this.locks.has(t);)await this.locks.get(t);let e=()=>{},i=new Promise(s=>{e=s;});return this.locks.set(t,i),()=>{this.locks.delete(t),e();}}async initCollection(t){if(this.collections.has(t))return;let e=this.getFilePath(t),i=this.getIndexFilePath(t);if(d.existsSync(i))try{let s=await d.promises.readFile(i,"utf-8"),n=JSON.parse(s),r={name:t,createdAt:n.createdAt,updatedAt:n.updatedAt,index:new Map(n.ids.map((o,a)=>[o,{id:o,offset:a}])),count:n.ids.length,dirty:!1};this.collections.set(t,r),this.documentCache.set(t,new S(this.cacheSize));return}catch{}if(d.existsSync(e))try{let s=await d.promises.readFile(e,"utf-8"),n=JSON.parse(s),r=new Map;n.documents.forEach((u,F)=>{r.set(u._id,{id:u._id,offset:F});});let o={name:t,createdAt:n.createdAt,updatedAt:n.updatedAt,index:r,count:n.documents.length,dirty:!1};this.collections.set(t,o),this.documentCache.set(t,new S(this.cacheSize));let a=this.documentCache.get(t),l=Math.min(n.documents.length,this.cacheSize);for(let u=0;u<l;u++)a.set(n.documents[u]._id,n.documents[u]);await this.saveIndex(t);}catch(s){throw new f(`Failed to initialize collection "${t}"`,s)}else {let s={name:t,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString(),index:new Map,count:0,dirty:false};this.collections.set(t,s),this.documentCache.set(t,new S(this.cacheSize));}}async saveIndex(t){let e=this.collections.get(t);if(!e)return;let i=this.getIndexFilePath(t),s={name:t,createdAt:e.createdAt,updatedAt:e.updatedAt,ids:Array.from(e.index.keys())},n=this.prettyPrint?JSON.stringify(s,null,2):JSON.stringify(s);await d.promises.writeFile(i,n,"utf-8");}getCount(t){return this.collections.get(t)?.count??0}hasDocument(t,e){return this.collections.get(t)?.index.has(e)??false}getDocumentIds(t){let e=this.collections.get(t);return e?Array.from(e.index.keys()):[]}async getDocument(t,e){await this.initCollection(t);let i=this.collections.get(t);if(!i||!i.index.has(e))return null;let s=this.documentCache.get(t),n=s.get(e);if(n)return n;let r=await this.loadDocumentFromDisk(t,e);return r&&s.set(e,r),r}async loadDocumentFromDisk(t,e){let i=this.getFilePath(t);try{let s=await d.promises.readFile(i,"utf-8");return JSON.parse(s).documents.find(r=>r._id===e)??null}catch{return null}}async getDocuments(t,e){await this.initCollection(t);let i=this.documentCache.get(t),s=[],n=[];for(let r of e){let o=i.get(r);o?s.push(o):n.push(r);}if(n.length>0){let r=await this.loadDocumentsFromDisk(t,n);for(let o of r)i.set(o._id,o),s.push(o);}return s}async loadDocumentsFromDisk(t,e){let i=this.getFilePath(t),s=new Set(e);try{let n=await d.promises.readFile(i,"utf-8");return JSON.parse(n).documents.filter(o=>s.has(o._id))}catch{return []}}async getAllDocuments(t){await this.initCollection(t);let e=this.getFilePath(t);try{if(!d.existsSync(e))return [];let i=await d.promises.readFile(e,"utf-8"),s=JSON.parse(i),n=this.documentCache.get(t);for(let r of s.documents)n.set(r._id,r);return s.documents}catch{return []}}async insertDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);s.index.set(e._id,{id:e._id,offset:s.count}),s.count++,s.updatedAt=new Date().toISOString(),s.dirty=!0,n.set(e._id,e),await this.persistCollection(t);}finally{i();}}async insertDocuments(t,e){if(e.length===0)return;await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);for(let r of e)s.index.set(r._id,{id:r._id,offset:s.count}),s.count++,n.set(r._id,r);s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async updateDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);if(!s.index.has(e._id))return;n.set(e._id,e),s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async deleteDocument(t,e){await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);if(!s.index.has(e))return;s.index.delete(e),s.count--,n.delete(e),s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async deleteDocuments(t,e){if(e.length===0)return;await this.initCollection(t);let i=await this.acquireLock(t);try{let s=this.collections.get(t),n=this.documentCache.get(t);for(let r of e)s.index.has(r)&&(s.index.delete(r),s.count--,n.delete(r));s.updatedAt=new Date().toISOString(),s.dirty=!0,await this.persistCollection(t);}finally{i();}}async clearCollection(t){await this.initCollection(t);let e=await this.acquireLock(t);try{let i=this.collections.get(t),s=this.documentCache.get(t);i.index.clear(),i.count=0,s.clear(),i.updatedAt=new Date().toISOString(),i.dirty=!0,await this.persistCollection(t);}finally{e();}}async persistCollection(t){let e=this.collections.get(t);if(!e)return;let i=this.getFilePath(t),s=`${i}.tmp.${Date.now()}`;try{let n=await this.buildDocumentArray(t),r={name:t,documents:n,createdAt:e.createdAt,updatedAt:e.updatedAt},o=this.prettyPrint?JSON.stringify(r,null,2):JSON.stringify(r);await d.promises.writeFile(s,o,"utf-8"),await d.promises.rename(s,i),await this.saveIndex(t),e.dirty=!1;}catch(n){try{d.existsSync(s)&&await d.promises.unlink(s);}catch{}throw new f(`Failed to persist collection "${t}"`,n)}}async buildDocumentArray(t){let e=this.collections.get(t);if(!e||e.count===0)return [];let i=this.getFilePath(t),s=[];try{if(d.existsSync(i)){let a=await d.promises.readFile(i,"utf-8");s=JSON.parse(a).documents;}}catch{}let n=new Map;for(let a of s)n.set(a._id,a);this.documentCache.get(t).forEach((a,l)=>{n.set(l,a);});let o=[];for(let a of e.index.keys()){let l=n.get(a);l&&o.push(l);}return o}async deleteCollection(t){let e=await this.acquireLock(t);try{this.collections.delete(t),this.documentCache.delete(t);let i=this.getFilePath(t),s=this.getIndexFilePath(t);d.existsSync(i)&&await d.promises.unlink(i),d.existsSync(s)&&await d.promises.unlink(s);}finally{e();}}async exists(t){if(this.collections.has(t))return true;let e=this.getFilePath(t);return d.existsSync(e)}async list(){try{let t=await d.promises.readdir(this.dataDir),e=new Set;for(let i of t)i.includes(".index")||i.includes("_chunk")||i.endsWith(this.fileExtension)&&e.add(i.slice(0,-this.fileExtension.length));return Array.from(e)}catch{return []}}getStats(){let t=0;for(let e of this.documentCache.values())t+=e.size;return {collections:this.collections.size,cachedDocuments:t,cacheSize:this.cacheSize}}clearCache(t){if(t)this.documentCache.get(t)?.clear();else for(let e of this.documentCache.values())e.clear();}async flush(){for(let[t,e]of this.collections.entries())e.dirty&&await this.persistCollection(t);}getDataDir(){return this.dataDir}};var j=class{name;storage;queryEngine;idGenerator;schema;constructor(t,e,i={}){this.name=t,this.storage=e,this.queryEngine=new D,this.idGenerator=i.idGenerator??v,this.schema=i.schema;}validate(t){if(!this.schema)return t;let e=this.schema.safeParse(t);if(!e.success)throw new T(this.name,e.error.issues);return e.data}async insert(t){let e=t._id||this.idGenerator();if(this.storage.hasDocument(this.name,e))throw new w(this.name,e);let i={...h(t),_id:e},s=this.validate(i);return await this.storage.insertDocument(this.name,s),h(s)}async insertFast(t){let e=t._id||this.idGenerator(),i={...h(t),_id:e},s=this.validate(i);return await this.storage.insertDocument(this.name,s),h(s)}async insertMany(t){if(t.length===0)return [];let e=new Set(this.storage.getDocumentIds(this.name)),i=[];for(let s of t){let n=s._id||this.idGenerator();if(e.has(n))throw new w(this.name,n);e.add(n);let r={...h(s),_id:n},o=this.validate(r);i.push(o);}return await this.storage.insertDocuments(this.name,i),i.map(s=>h(s))}async find(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);return e?.sort&&(s=this.sortDocuments(s,e.sort)),e?.skip&&e.skip>0&&(s=s.slice(e.skip)),e?.limit&&e.limit>0&&(s=s.slice(0,e.limit)),e?.projection?s.map(n=>O(h(n),e.projection)):s.map(n=>h(n))}async findOne(t){let e=await this.find(t,{limit:1});return e.length>0?e[0]:null}async findById(t){let e=await this.storage.getDocument(this.name,t);return e?h(e):null}async count(t){if(!t||Object.keys(t).length===0)return this.storage.getCount(this.name);let e=await this.storage.getAllDocuments(this.name);return this.queryEngine.filter(e,t).length}async update(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return 0;for(let n of s)this.applyUpdate(n,e),await this.storage.updateDocument(this.name,n);return s.length}async updateOne(t,e){let i=await this.storage.getAllDocuments(this.name),s=this.queryEngine.filter(i,t);if(s.length===0)return null;let n=s[0];return this.applyUpdate(n,e),await this.storage.updateDocument(this.name,n),h(n)}async updateById(t,e){let i=await this.storage.getDocument(this.name,t);return i?(this.applyUpdate(i,e),await this.storage.updateDocument(this.name,i),h(i)):null}async delete(t){let e=await this.storage.getAllDocuments(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return 0;let s=i.map(n=>n._id);return await this.storage.deleteDocuments(this.name,s),i.length}async deleteOne(t){let e=await this.storage.getAllDocuments(this.name),i=this.queryEngine.filter(e,t);if(i.length===0)return null;let s=i[0];return await this.storage.deleteDocument(this.name,s._id),h(s)}async deleteById(t){let e=await this.storage.getDocument(this.name,t);return e?(await this.storage.deleteDocument(this.name,t),h(e)):null}async getAll(){return this.find()}async clear(){await this.storage.clearCollection(this.name);}async drop(){await this.storage.deleteCollection(this.name);}async flush(){await this.storage.flush();}getName(){return this.name}applyUpdate(t,e){if(!Object.keys(e).some(n=>n.startsWith("$"))){Object.assign(t,e);return}let s=e;if(s.$set)for(let[n,r]of Object.entries(s.$set))n!=="_id"&&k(t,n,r);if(s.$unset)for(let n of Object.keys(s.$unset))n!=="_id"&&b(t,n);if(s.$inc)for(let[n,r]of Object.entries(s.$inc)){let o=t[n];typeof o=="number"&&typeof r=="number"&&(t[n]=o+r);}if(s.$push)for(let[n,r]of Object.entries(s.$push)){let o=t[n];Array.isArray(o)&&o.push(r);}if(s.$pull)for(let[n,r]of Object.entries(s.$pull)){let o=t[n];if(Array.isArray(o)){let a=o.findIndex(l=>JSON.stringify(l)===JSON.stringify(r));a!==-1&&o.splice(a,1);}}if(s.$addToSet)for(let[n,r]of Object.entries(s.$addToSet)){let o=t[n];Array.isArray(o)&&(o.some(l=>JSON.stringify(l)===JSON.stringify(r))||o.push(r));}}sortDocuments(t,e){let i=Object.entries(e);return [...t].sort((s,n)=>{for(let[r,o]of i){let a=s[r],l=n[r],u=0;if(a===l?u=0:a==null?u=1:l==null?u=-1:typeof a=="number"&&typeof l=="number"?u=a-l:typeof a=="string"&&typeof l=="string"?u=a.localeCompare(l):a instanceof Date&&l instanceof Date?u=a.getTime()-l.getTime():u=String(a).localeCompare(String(l)),u!==0)return u*(o===-1||o==="desc"?-1:1)}return 0})}};var G={autoSave:true,saveDebounce:0,prettyPrint:true,fileExtension:".json"},J=class{options;storage;hcStorage;lazyStorage;collections=new Map;isHighConcurrency;isLazyLoading;connected=false;constructor(t){if(this.options={...G,...t},this.isHighConcurrency=t.highConcurrency?.enabled??false,this.isLazyLoading=t.lazyLoading?.enabled??false,this.isHighConcurrency&&this.isLazyLoading)throw new Error("Cannot enable both highConcurrency and lazyLoading modes simultaneously");this.isHighConcurrency?(this.storage=null,this.hcStorage=new _(this.options),this.lazyStorage=null):this.isLazyLoading?(this.storage=null,this.hcStorage=null,this.lazyStorage=new L(this.options)):(this.storage=new z(this.options),this.hcStorage=null,this.lazyStorage=null);}getStorage(){if(this.storage===null)throw new Error("Storage is not available in high-concurrency or lazy loading mode");return this.storage}getHCStorage(){if(this.hcStorage===null)throw new Error("HighConcurrencyStorage is not available in standard or lazy loading mode");return this.hcStorage}getLazyStorage(){if(this.lazyStorage===null)throw new Error("LazyStorage is not available in standard or high-concurrency mode");return this.lazyStorage}async connect(){if(!this.connected){if(this.isHighConcurrency){let t=await this.getHCStorage().listCollections();for(let e of t)this.getOrCreateCollection(e);}else if(this.isLazyLoading){let t=await this.getLazyStorage().list();for(let e of t)this.getOrCreateCollection(e);}else {let t=await this.getStorage().list();for(let e of t)this.getOrCreateCollection(e);}this.connected=true;}}async close(){if(this.connected){for(let t of this.collections.values())await t.flush();this.isHighConcurrency&&this.hcStorage&&await this.hcStorage.shutdown(),this.collections.clear(),this.storage&&this.storage.clearCache(),this.hcStorage&&this.hcStorage.clearCache(),this.lazyStorage&&this.lazyStorage.clearCache(),this.connected=false;}}collection(t,e){if(!t||typeof t!="string")throw new C("Collection name must be a non-empty string");if(!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(t))throw new C("Collection name must start with a letter or underscore and contain only letters, numbers, underscores, and hyphens");return this.getOrCreateCollection(t,e)}getOrCreateCollection(t,e){if(this.collections.has(t))return this.collections.get(t);if(this.isHighConcurrency){let i=new Q(t,this.getHCStorage(),{schema:e?.schema});this.collections.set(t,i);}else if(this.isLazyLoading){let i=new j(t,this.getLazyStorage(),{schema:e?.schema});this.collections.set(t,i);}else {let i=new $(t,this.getStorage(),{autoSave:this.options.autoSave,saveDebounce:this.options.saveDebounce,schema:e?.schema});this.collections.set(t,i);}return this.collections.get(t)}async hasCollection(t){return this.isHighConcurrency?this.getHCStorage().exists(t):this.getStorage().exists(t)}async listCollections(){return this.isHighConcurrency?this.getHCStorage().listCollections():this.getStorage().list()}async dropCollection(t){let e=this.collections.get(t);e?(await e.drop(),this.collections.delete(t)):this.isHighConcurrency?await this.getHCStorage().deleteCollection(t):await this.getStorage().delete(t);}async drop(){if(this.isHighConcurrency){let t=this.getHCStorage(),e=await t.listCollections();for(let i of e)await t.deleteCollection(i);}else {let t=this.getStorage(),e=await t.list();for(let i of e)await t.delete(i);}this.collections.clear(),this.storage?.clearCache(),this.hcStorage?.clearCache();}getDataDir(){return this.isHighConcurrency?this.options.dataDir:this.getStorage().getDataDir()}isConnected(){return this.connected}isHighConcurrencyMode(){return this.isHighConcurrency}getStats(){return !this.isHighConcurrency||!this.hcStorage?null:this.hcStorage.getStats()}async flush(){for(let t of this.collections.values())await t.flush();}};export{$ as Collection,C as CollectionError,R as DocumentNotFoundError,w as DuplicateKeyError,Q as HighConcurrencyCollection,_ as HighConcurrencyStorage,J as JsonDB,P as JsonDBError,E as PartitionManager,f as StorageError,T as ValidationError,I as WorkerPool,A as WriteQueue,v as generateId,U as isValidId,W as parallelLimit};
|
package/package.json
CHANGED
|
@@ -1,88 +1,97 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "nodejs-json-db",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A production-ready, lightweight JSON-based database for Node.js and Electron applications",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"types": "./dist/index.d.ts",
|
|
7
|
-
"module": "./dist/index.mjs",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"require": "./dist/index.js",
|
|
12
|
-
"import": "./dist/index.mjs",
|
|
13
|
-
"default": "./dist/index.js"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"sideEffects": false,
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "tsup",
|
|
19
|
-
"test": "vitest run",
|
|
20
|
-
"test:watch": "vitest",
|
|
21
|
-
"test:coverage": "vitest run --coverage",
|
|
22
|
-
"dev": "tsup --watch",
|
|
23
|
-
"lint": "eslint src --ext .ts",
|
|
24
|
-
"format": "prettier --write \"src/**/*.ts\"",
|
|
25
|
-
"typecheck": "tsc --noEmit",
|
|
26
|
-
"prepublishOnly": "npm run build && npm run test",
|
|
27
|
-
"benchmark": "tsx examples/benchmark.ts"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"@
|
|
66
|
-
"@
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"
|
|
79
|
-
},
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
},
|
|
83
|
-
"
|
|
84
|
-
"zod":
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "nodejs-json-db",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A production-ready, lightweight JSON-based database for Node.js and Electron applications",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"lint": "eslint src --ext .ts",
|
|
24
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"prepublishOnly": "npm run build && npm run test",
|
|
27
|
+
"benchmark": "tsx examples/benchmark.ts",
|
|
28
|
+
"prepare": "husky"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/iqbal-rashed/nodejs-json-db.git"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"json",
|
|
41
|
+
"database",
|
|
42
|
+
"db",
|
|
43
|
+
"nosql",
|
|
44
|
+
"document",
|
|
45
|
+
"storage",
|
|
46
|
+
"electron",
|
|
47
|
+
"nodejs",
|
|
48
|
+
"typescript",
|
|
49
|
+
"esm",
|
|
50
|
+
"cjs",
|
|
51
|
+
"lightweight",
|
|
52
|
+
"file-based",
|
|
53
|
+
"local-storage"
|
|
54
|
+
],
|
|
55
|
+
"author": "Rashed Iqbal",
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/iqbal-rashed/nodejs-json-db/issues"
|
|
59
|
+
},
|
|
60
|
+
"homepage": "https://github.com/iqbal-rashed/nodejs-json-db#readme",
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18.0.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@eslint/js": "^9.18.0",
|
|
66
|
+
"@types/node": "^22.10.5",
|
|
67
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
68
|
+
"eslint": "^9.18.0",
|
|
69
|
+
"globals": "^15.14.0",
|
|
70
|
+
"husky": "^9.1.7",
|
|
71
|
+
"lint-staged": "^16.2.7",
|
|
72
|
+
"prettier": "^3.4.2",
|
|
73
|
+
"tsup": "^8.5.1",
|
|
74
|
+
"tsx": "^4.21.0",
|
|
75
|
+
"typescript": "^5.9.3",
|
|
76
|
+
"typescript-eslint": "^8.19.1",
|
|
77
|
+
"vitest": "^4.0.16",
|
|
78
|
+
"zod": "^4.2.1"
|
|
79
|
+
},
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"bson": "^7.0.0"
|
|
82
|
+
},
|
|
83
|
+
"peerDependencies": {
|
|
84
|
+
"zod": "^3.0.0 || ^4.0.0"
|
|
85
|
+
},
|
|
86
|
+
"peerDependenciesMeta": {
|
|
87
|
+
"zod": {
|
|
88
|
+
"optional": true
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"lint-staged": {
|
|
92
|
+
"*.ts": [
|
|
93
|
+
"eslint --fix",
|
|
94
|
+
"prettier --write"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|