@gravito/dark-matter 1.0.0-alpha.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 ADDED
@@ -0,0 +1,182 @@
1
+ # @gravito/dark-matter
2
+
3
+ > MongoDB client for Gravito - Bun native, Laravel-style API
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @gravito/dark-matter mongodb
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Mongo } from '@gravito/dark-matter'
15
+
16
+ // Configure
17
+ Mongo.configure({
18
+ default: 'main',
19
+ connections: {
20
+ main: { uri: 'mongodb://localhost:27017', database: 'myapp' }
21
+ }
22
+ })
23
+
24
+ // Connect
25
+ await Mongo.connect()
26
+
27
+ // Use
28
+ const users = await Mongo.collection('users')
29
+ .where('status', 'active')
30
+ .orderBy('createdAt', 'desc')
31
+ .limit(10)
32
+ .get()
33
+
34
+ // Disconnect
35
+ await Mongo.disconnect()
36
+ ```
37
+
38
+ ## Features
39
+
40
+ - 🚀 **Bun Native** - Optimized for Bun runtime
41
+ - 🎯 **Laravel-style API** - Familiar fluent interface
42
+ - 🔍 **Query Builder** - Type-safe query building
43
+ - 📊 **Aggregation Pipeline** - Fluent aggregation API
44
+ - 🔌 **Multi-connection** - Named connections support
45
+
46
+ ## API Reference
47
+
48
+ ### Query Builder
49
+
50
+ ```typescript
51
+ // Basic queries
52
+ const users = await Mongo.collection('users')
53
+ .where('status', 'active')
54
+ .where('age', '>', 18)
55
+ .whereIn('role', ['admin', 'editor'])
56
+ .get()
57
+
58
+ // Sorting & Pagination
59
+ const latest = await Mongo.collection('posts')
60
+ .orderBy('createdAt', 'desc')
61
+ .limit(10)
62
+ .skip(20)
63
+ .get()
64
+
65
+ // Select specific fields
66
+ const names = await Mongo.collection('users')
67
+ .select('name', 'email')
68
+ .get()
69
+
70
+ // Find by ID
71
+ const user = await Mongo.collection('users').find('507f1f77bcf86cd799439011')
72
+
73
+ // Count & Exists
74
+ const count = await Mongo.collection('users').where('status', 'active').count()
75
+ const exists = await Mongo.collection('users').where('email', 'john@example.com').exists()
76
+ ```
77
+
78
+ ### CRUD Operations
79
+
80
+ ```typescript
81
+ // Insert
82
+ const result = await Mongo.collection('users').insert({
83
+ name: 'John',
84
+ email: 'john@example.com',
85
+ createdAt: new Date()
86
+ })
87
+ console.log(result.insertedId)
88
+
89
+ // Insert Many
90
+ const results = await Mongo.collection('users').insertMany([
91
+ { name: 'Alice' },
92
+ { name: 'Bob' }
93
+ ])
94
+
95
+ // Update
96
+ await Mongo.collection('users')
97
+ .where('_id', userId)
98
+ .update({ status: 'inactive' })
99
+
100
+ // Update Many
101
+ await Mongo.collection('users')
102
+ .where('status', 'pending')
103
+ .updateMany({ status: 'active' })
104
+
105
+ // Delete
106
+ await Mongo.collection('users')
107
+ .where('_id', userId)
108
+ .delete()
109
+
110
+ // Delete Many
111
+ await Mongo.collection('users')
112
+ .where('status', 'deleted')
113
+ .deleteMany()
114
+ ```
115
+
116
+ ### Aggregation Pipeline
117
+
118
+ ```typescript
119
+ // Group and count
120
+ const stats = await Mongo.collection('orders')
121
+ .aggregate()
122
+ .match({ status: 'completed' })
123
+ .group({
124
+ _id: '$customerId',
125
+ totalOrders: { $sum: 1 },
126
+ totalAmount: { $sum: '$amount' }
127
+ })
128
+ .sort({ totalAmount: 'desc' })
129
+ .limit(10)
130
+ .get()
131
+
132
+ // Lookup (JOIN)
133
+ const ordersWithCustomers = await Mongo.collection('orders')
134
+ .aggregate()
135
+ .lookup({
136
+ from: 'customers',
137
+ localField: 'customerId',
138
+ foreignField: '_id',
139
+ as: 'customer'
140
+ })
141
+ .unwind('$customer')
142
+ .get()
143
+
144
+ // Project specific fields
145
+ const projected = await Mongo.collection('users')
146
+ .aggregate()
147
+ .project({
148
+ name: 1,
149
+ email: 1,
150
+ fullName: { $concat: ['$firstName', ' ', '$lastName'] }
151
+ })
152
+ .get()
153
+ ```
154
+
155
+ ### Multiple Connections
156
+
157
+ ```typescript
158
+ Mongo.configure({
159
+ default: 'main',
160
+ connections: {
161
+ main: { uri: 'mongodb://localhost:27017', database: 'app' },
162
+ analytics: { uri: 'mongodb://analytics.example.com:27017', database: 'analytics' }
163
+ }
164
+ })
165
+
166
+ // Use specific connection
167
+ const data = await Mongo.connection('analytics')
168
+ .collection('events')
169
+ .where('type', 'pageview')
170
+ .get()
171
+ ```
172
+
173
+ ## Roadmap
174
+
175
+ - [ ] Schema validation
176
+ - [ ] Transactions
177
+ - [ ] Change streams
178
+ - [ ] GridFS support
179
+
180
+ ## License
181
+
182
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,564 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __moduleCache = /* @__PURE__ */ new WeakMap;
19
+ var __toCommonJS = (from) => {
20
+ var entry = __moduleCache.get(from), desc;
21
+ if (entry)
22
+ return entry;
23
+ entry = __defProp({}, "__esModule", { value: true });
24
+ if (from && typeof from === "object" || typeof from === "function")
25
+ __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
26
+ get: () => from[key],
27
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
28
+ }));
29
+ __moduleCache.set(from, entry);
30
+ return entry;
31
+ };
32
+ var __export = (target, all) => {
33
+ for (var name in all)
34
+ __defProp(target, name, {
35
+ get: all[name],
36
+ enumerable: true,
37
+ configurable: true,
38
+ set: (newValue) => all[name] = () => newValue
39
+ });
40
+ };
41
+
42
+ // src/index.ts
43
+ var exports_src = {};
44
+ __export(exports_src, {
45
+ MongoQueryBuilder: () => MongoQueryBuilder,
46
+ MongoManager: () => MongoManager,
47
+ MongoClient: () => MongoClient,
48
+ MongoAggregateBuilder: () => MongoAggregateBuilder,
49
+ Mongo: () => Mongo
50
+ });
51
+ module.exports = __toCommonJS(exports_src);
52
+
53
+ // src/MongoQueryBuilder.ts
54
+ class MongoQueryBuilder {
55
+ nativeCollection;
56
+ collectionName;
57
+ filters = {};
58
+ orFilters = [];
59
+ projection = {};
60
+ sortSpec = {};
61
+ limitCount;
62
+ skipCount;
63
+ constructor(nativeCollection, collectionName) {
64
+ this.nativeCollection = nativeCollection;
65
+ this.collectionName = collectionName;
66
+ }
67
+ where(field, operatorOrValue, value) {
68
+ if (value === undefined) {
69
+ this.filters[field] = operatorOrValue;
70
+ } else {
71
+ const operator = operatorOrValue;
72
+ this.filters[field] = this.mapOperator(operator, value);
73
+ }
74
+ return this;
75
+ }
76
+ orWhere(field, operatorOrValue, value) {
77
+ const filter = {};
78
+ if (value === undefined) {
79
+ filter[field] = operatorOrValue;
80
+ } else {
81
+ const operator = operatorOrValue;
82
+ filter[field] = this.mapOperator(operator, value);
83
+ }
84
+ this.orFilters.push(filter);
85
+ return this;
86
+ }
87
+ whereIn(field, values) {
88
+ this.filters[field] = { $in: values };
89
+ return this;
90
+ }
91
+ whereNotIn(field, values) {
92
+ this.filters[field] = { $nin: values };
93
+ return this;
94
+ }
95
+ whereNull(field) {
96
+ this.filters[field] = null;
97
+ return this;
98
+ }
99
+ whereNotNull(field) {
100
+ this.filters[field] = { $ne: null };
101
+ return this;
102
+ }
103
+ whereExists(field, exists = true) {
104
+ this.filters[field] = { $exists: exists };
105
+ return this;
106
+ }
107
+ whereRegex(field, pattern) {
108
+ this.filters[field] = { $regex: pattern };
109
+ return this;
110
+ }
111
+ select(...fields) {
112
+ for (const field of fields) {
113
+ this.projection[field] = 1;
114
+ }
115
+ return this;
116
+ }
117
+ exclude(...fields) {
118
+ for (const field of fields) {
119
+ this.projection[field] = 0;
120
+ }
121
+ return this;
122
+ }
123
+ orderBy(field, direction = "asc") {
124
+ this.sortSpec[field] = direction === "asc" || direction === 1 ? 1 : -1;
125
+ return this;
126
+ }
127
+ latest(field = "createdAt") {
128
+ return this.orderBy(field, "desc");
129
+ }
130
+ oldest(field = "createdAt") {
131
+ return this.orderBy(field, "asc");
132
+ }
133
+ limit(count) {
134
+ this.limitCount = count;
135
+ return this;
136
+ }
137
+ skip(count) {
138
+ this.skipCount = count;
139
+ return this;
140
+ }
141
+ offset(count) {
142
+ return this.skip(count);
143
+ }
144
+ async get() {
145
+ const cursor = this.nativeCollection.find(this.toFilter(), {
146
+ projection: Object.keys(this.projection).length > 0 ? this.projection : undefined,
147
+ sort: Object.keys(this.sortSpec).length > 0 ? this.sortSpec : undefined,
148
+ limit: this.limitCount,
149
+ skip: this.skipCount
150
+ });
151
+ return await cursor.toArray();
152
+ }
153
+ async first() {
154
+ const result = await this.nativeCollection.findOne(this.toFilter(), {
155
+ projection: Object.keys(this.projection).length > 0 ? this.projection : undefined,
156
+ sort: Object.keys(this.sortSpec).length > 0 ? this.sortSpec : undefined
157
+ });
158
+ return result;
159
+ }
160
+ async find(id) {
161
+ const { ObjectId } = await this.loadObjectId();
162
+ const result = await this.nativeCollection.findOne({ _id: new ObjectId(id) }, { projection: Object.keys(this.projection).length > 0 ? this.projection : undefined });
163
+ return result;
164
+ }
165
+ async count() {
166
+ return await this.nativeCollection.countDocuments(this.toFilter());
167
+ }
168
+ async exists() {
169
+ const count = await this.nativeCollection.countDocuments(this.toFilter(), { limit: 1 });
170
+ return count > 0;
171
+ }
172
+ async distinct(field) {
173
+ return await this.nativeCollection.distinct(field, this.toFilter());
174
+ }
175
+ async insert(document) {
176
+ const result = await this.nativeCollection.insertOne(document);
177
+ return {
178
+ insertedId: result.insertedId.toString(),
179
+ acknowledged: result.acknowledged
180
+ };
181
+ }
182
+ async insertMany(documents) {
183
+ const result = await this.nativeCollection.insertMany(documents);
184
+ return {
185
+ insertedIds: Object.values(result.insertedIds).map((id) => id.toString()),
186
+ insertedCount: result.insertedCount,
187
+ acknowledged: result.acknowledged
188
+ };
189
+ }
190
+ async update(update) {
191
+ const updateDoc = this.normalizeUpdate(update);
192
+ const result = await this.nativeCollection.updateOne(this.toFilter(), updateDoc);
193
+ return {
194
+ matchedCount: result.matchedCount,
195
+ modifiedCount: result.modifiedCount,
196
+ acknowledged: result.acknowledged,
197
+ ...result.upsertedId ? { upsertedId: result.upsertedId.toString() } : {}
198
+ };
199
+ }
200
+ async updateMany(update) {
201
+ const updateDoc = this.normalizeUpdate(update);
202
+ const result = await this.nativeCollection.updateMany(this.toFilter(), updateDoc);
203
+ return {
204
+ matchedCount: result.matchedCount,
205
+ modifiedCount: result.modifiedCount,
206
+ acknowledged: result.acknowledged,
207
+ ...result.upsertedId ? { upsertedId: result.upsertedId.toString() } : {}
208
+ };
209
+ }
210
+ async delete() {
211
+ const result = await this.nativeCollection.deleteOne(this.toFilter());
212
+ return {
213
+ deletedCount: result.deletedCount,
214
+ acknowledged: result.acknowledged
215
+ };
216
+ }
217
+ async deleteMany() {
218
+ const result = await this.nativeCollection.deleteMany(this.toFilter());
219
+ return {
220
+ deletedCount: result.deletedCount,
221
+ acknowledged: result.acknowledged
222
+ };
223
+ }
224
+ aggregate() {
225
+ return new MongoAggregateBuilder(this.nativeCollection, this.toFilter());
226
+ }
227
+ toFilter() {
228
+ if (this.orFilters.length === 0) {
229
+ return { ...this.filters };
230
+ }
231
+ return {
232
+ $or: [this.filters, ...this.orFilters]
233
+ };
234
+ }
235
+ clone() {
236
+ const cloned = new MongoQueryBuilder(this.nativeCollection, this.collectionName);
237
+ cloned.filters = { ...this.filters };
238
+ cloned.orFilters = [...this.orFilters];
239
+ cloned.projection = { ...this.projection };
240
+ cloned.sortSpec = { ...this.sortSpec };
241
+ cloned.limitCount = this.limitCount;
242
+ cloned.skipCount = this.skipCount;
243
+ return cloned;
244
+ }
245
+ mapOperator(operator, value) {
246
+ switch (operator) {
247
+ case "=":
248
+ return value;
249
+ case "!=":
250
+ return { $ne: value };
251
+ case ">":
252
+ return { $gt: value };
253
+ case ">=":
254
+ return { $gte: value };
255
+ case "<":
256
+ return { $lt: value };
257
+ case "<=":
258
+ return { $lte: value };
259
+ case "in":
260
+ return { $in: value };
261
+ case "nin":
262
+ return { $nin: value };
263
+ case "exists":
264
+ return { $exists: value };
265
+ case "regex":
266
+ return { $regex: value };
267
+ default:
268
+ return value;
269
+ }
270
+ }
271
+ normalizeUpdate(update) {
272
+ const hasOperator = Object.keys(update).some((key) => key.startsWith("$"));
273
+ if (hasOperator) {
274
+ return update;
275
+ }
276
+ return { $set: update };
277
+ }
278
+ async loadObjectId() {
279
+ const mongodb = await import("mongodb");
280
+ return mongodb;
281
+ }
282
+ }
283
+
284
+ class MongoAggregateBuilder {
285
+ nativeCollection;
286
+ pipeline = [];
287
+ constructor(nativeCollection, initialFilter) {
288
+ this.nativeCollection = nativeCollection;
289
+ if (initialFilter && Object.keys(initialFilter).length > 0) {
290
+ this.pipeline.push({ $match: initialFilter });
291
+ }
292
+ }
293
+ match(filter) {
294
+ this.pipeline.push({ $match: filter });
295
+ return this;
296
+ }
297
+ group(spec) {
298
+ this.pipeline.push({ $group: spec });
299
+ return this;
300
+ }
301
+ project(projection) {
302
+ this.pipeline.push({ $project: projection });
303
+ return this;
304
+ }
305
+ sort(spec) {
306
+ const normalizedSpec = {};
307
+ for (const [key, value] of Object.entries(spec)) {
308
+ normalizedSpec[key] = value === "asc" || value === 1 ? 1 : -1;
309
+ }
310
+ this.pipeline.push({ $sort: normalizedSpec });
311
+ return this;
312
+ }
313
+ limit(count) {
314
+ this.pipeline.push({ $limit: count });
315
+ return this;
316
+ }
317
+ skip(count) {
318
+ this.pipeline.push({ $skip: count });
319
+ return this;
320
+ }
321
+ unwind(field) {
322
+ this.pipeline.push({ $unwind: field });
323
+ return this;
324
+ }
325
+ lookup(options) {
326
+ this.pipeline.push({ $lookup: options });
327
+ return this;
328
+ }
329
+ addFields(fields) {
330
+ this.pipeline.push({ $addFields: fields });
331
+ return this;
332
+ }
333
+ count(fieldName) {
334
+ this.pipeline.push({ $count: fieldName });
335
+ return this;
336
+ }
337
+ async get() {
338
+ const cursor = this.nativeCollection.aggregate(this.pipeline);
339
+ return await cursor.toArray();
340
+ }
341
+ async first() {
342
+ this.pipeline.push({ $limit: 1 });
343
+ const results = await this.get();
344
+ return results[0] ?? null;
345
+ }
346
+ toPipeline() {
347
+ return [...this.pipeline];
348
+ }
349
+ }
350
+
351
+ // src/MongoClient.ts
352
+ class MongoClient {
353
+ config;
354
+ client = null;
355
+ db = null;
356
+ connected = false;
357
+ mongodb = null;
358
+ constructor(config = {}) {
359
+ this.config = config;
360
+ }
361
+ async connect() {
362
+ if (this.connected) {
363
+ return;
364
+ }
365
+ this.mongodb = await this.loadMongoDBModule();
366
+ const uri = this.buildConnectionUri();
367
+ const options = {
368
+ maxPoolSize: this.config.maxPoolSize ?? 10,
369
+ minPoolSize: this.config.minPoolSize ?? 1
370
+ };
371
+ if (this.config.connectTimeoutMS) {
372
+ options.connectTimeoutMS = this.config.connectTimeoutMS;
373
+ }
374
+ if (this.config.socketTimeoutMS) {
375
+ options.socketTimeoutMS = this.config.socketTimeoutMS;
376
+ }
377
+ this.client = new this.mongodb.MongoClient(uri, options);
378
+ await this.client.connect();
379
+ const dbName = this.config.database ?? "test";
380
+ this.db = this.client.db(dbName);
381
+ this.connected = true;
382
+ }
383
+ async disconnect() {
384
+ if (this.client) {
385
+ await this.client.close();
386
+ this.client = null;
387
+ this.db = null;
388
+ }
389
+ this.connected = false;
390
+ }
391
+ isConnected() {
392
+ return this.connected && this.client !== null;
393
+ }
394
+ database(name) {
395
+ const client = this.getClient();
396
+ const db = name ? client.db(name) : this.db;
397
+ return new MongoDatabaseWrapper(db);
398
+ }
399
+ collection(name) {
400
+ const db = this.getDatabase();
401
+ const nativeCollection = db.collection(name);
402
+ return new MongoQueryBuilder(nativeCollection, name);
403
+ }
404
+ buildConnectionUri() {
405
+ if (this.config.uri) {
406
+ return this.config.uri;
407
+ }
408
+ const host = this.config.host ?? "localhost";
409
+ const port = this.config.port ?? 27017;
410
+ const protocol = this.config.tls ? "mongodb+srv" : "mongodb";
411
+ let uri = `${protocol}://`;
412
+ if (this.config.username && this.config.password) {
413
+ uri += `${encodeURIComponent(this.config.username)}:${encodeURIComponent(this.config.password)}@`;
414
+ }
415
+ uri += `${host}`;
416
+ if (!this.config.tls) {
417
+ uri += `:${port}`;
418
+ }
419
+ if (this.config.database) {
420
+ uri += `/${this.config.database}`;
421
+ }
422
+ const params = [];
423
+ if (this.config.authSource) {
424
+ params.push(`authSource=${this.config.authSource}`);
425
+ }
426
+ if (this.config.replicaSet) {
427
+ params.push(`replicaSet=${this.config.replicaSet}`);
428
+ }
429
+ if (params.length > 0) {
430
+ uri += `?${params.join("&")}`;
431
+ }
432
+ return uri;
433
+ }
434
+ async loadMongoDBModule() {
435
+ try {
436
+ const mongodb = await import("mongodb");
437
+ return mongodb;
438
+ } catch {
439
+ throw new Error('MongoDB client requires the "mongodb" package. Please install it: bun add mongodb');
440
+ }
441
+ }
442
+ getClient() {
443
+ if (!this.client) {
444
+ throw new Error("MongoDB client not connected. Call connect() first.");
445
+ }
446
+ return this.client;
447
+ }
448
+ getDatabase() {
449
+ if (!this.db) {
450
+ throw new Error("MongoDB client not connected. Call connect() first.");
451
+ }
452
+ return this.db;
453
+ }
454
+ }
455
+
456
+ class MongoDatabaseWrapper {
457
+ db;
458
+ constructor(db) {
459
+ this.db = db;
460
+ }
461
+ collection(name) {
462
+ const nativeCollection = this.db.collection(name);
463
+ return new MongoQueryBuilder(nativeCollection, name);
464
+ }
465
+ async listCollections() {
466
+ const collections = await this.db.listCollections().toArray();
467
+ return collections.map((c) => c.name);
468
+ }
469
+ async dropCollection(name) {
470
+ return await this.db.dropCollection(name);
471
+ }
472
+ async createCollection(name) {
473
+ await this.db.createCollection(name);
474
+ }
475
+ }
476
+
477
+ // src/MongoManager.ts
478
+ class MongoManager {
479
+ connections = new Map;
480
+ defaultConnection = "default";
481
+ configs = new Map;
482
+ configure(config) {
483
+ this.defaultConnection = config.default ?? "default";
484
+ for (const [name, connectionConfig] of Object.entries(config.connections)) {
485
+ this.configs.set(name, connectionConfig);
486
+ }
487
+ }
488
+ addConnection(name, config) {
489
+ this.configs.set(name, config);
490
+ }
491
+ connection(name) {
492
+ const connectionName = name ?? this.defaultConnection;
493
+ if (!this.connections.has(connectionName)) {
494
+ const config = this.configs.get(connectionName);
495
+ if (!config) {
496
+ throw new Error(`MongoDB connection "${connectionName}" not configured`);
497
+ }
498
+ this.connections.set(connectionName, new MongoClient(config));
499
+ }
500
+ return this.connections.get(connectionName);
501
+ }
502
+ getDefault() {
503
+ return this.connection(this.defaultConnection);
504
+ }
505
+ async connectAll() {
506
+ for (const [name] of this.configs) {
507
+ const client = this.connection(name);
508
+ await client.connect();
509
+ }
510
+ }
511
+ async disconnectAll() {
512
+ for (const client of this.connections.values()) {
513
+ await client.disconnect();
514
+ }
515
+ this.connections.clear();
516
+ }
517
+ hasConnection(name) {
518
+ return this.configs.has(name);
519
+ }
520
+ async removeConnection(name) {
521
+ const client = this.connections.get(name);
522
+ if (client) {
523
+ await client.disconnect();
524
+ this.connections.delete(name);
525
+ }
526
+ this.configs.delete(name);
527
+ }
528
+ }
529
+
530
+ // src/Mongo.ts
531
+ var manager = new MongoManager;
532
+
533
+ class Mongo {
534
+ static configure(config) {
535
+ manager.configure(config);
536
+ }
537
+ static addConnection(name, config) {
538
+ manager.addConnection(name, config);
539
+ }
540
+ static connection(name) {
541
+ return manager.connection(name);
542
+ }
543
+ static async connect() {
544
+ await manager.getDefault().connect();
545
+ }
546
+ static async connectAll() {
547
+ await manager.connectAll();
548
+ }
549
+ static async disconnect() {
550
+ await manager.getDefault().disconnect();
551
+ }
552
+ static async disconnectAll() {
553
+ await manager.disconnectAll();
554
+ }
555
+ static isConnected() {
556
+ return manager.getDefault().isConnected();
557
+ }
558
+ static database(name) {
559
+ return manager.getDefault().database(name);
560
+ }
561
+ static collection(name) {
562
+ return manager.getDefault().collection(name);
563
+ }
564
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,538 @@
1
+ import { createRequire } from "node:module";
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __toESM = (mod, isNodeMode, target) => {
8
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
9
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
+ for (let key of __getOwnPropNames(mod))
11
+ if (!__hasOwnProp.call(to, key))
12
+ __defProp(to, key, {
13
+ get: () => mod[key],
14
+ enumerable: true
15
+ });
16
+ return to;
17
+ };
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // src/MongoQueryBuilder.ts
21
+ class MongoQueryBuilder {
22
+ nativeCollection;
23
+ collectionName;
24
+ filters = {};
25
+ orFilters = [];
26
+ projection = {};
27
+ sortSpec = {};
28
+ limitCount;
29
+ skipCount;
30
+ constructor(nativeCollection, collectionName) {
31
+ this.nativeCollection = nativeCollection;
32
+ this.collectionName = collectionName;
33
+ }
34
+ where(field, operatorOrValue, value) {
35
+ if (value === undefined) {
36
+ this.filters[field] = operatorOrValue;
37
+ } else {
38
+ const operator = operatorOrValue;
39
+ this.filters[field] = this.mapOperator(operator, value);
40
+ }
41
+ return this;
42
+ }
43
+ orWhere(field, operatorOrValue, value) {
44
+ const filter = {};
45
+ if (value === undefined) {
46
+ filter[field] = operatorOrValue;
47
+ } else {
48
+ const operator = operatorOrValue;
49
+ filter[field] = this.mapOperator(operator, value);
50
+ }
51
+ this.orFilters.push(filter);
52
+ return this;
53
+ }
54
+ whereIn(field, values) {
55
+ this.filters[field] = { $in: values };
56
+ return this;
57
+ }
58
+ whereNotIn(field, values) {
59
+ this.filters[field] = { $nin: values };
60
+ return this;
61
+ }
62
+ whereNull(field) {
63
+ this.filters[field] = null;
64
+ return this;
65
+ }
66
+ whereNotNull(field) {
67
+ this.filters[field] = { $ne: null };
68
+ return this;
69
+ }
70
+ whereExists(field, exists = true) {
71
+ this.filters[field] = { $exists: exists };
72
+ return this;
73
+ }
74
+ whereRegex(field, pattern) {
75
+ this.filters[field] = { $regex: pattern };
76
+ return this;
77
+ }
78
+ select(...fields) {
79
+ for (const field of fields) {
80
+ this.projection[field] = 1;
81
+ }
82
+ return this;
83
+ }
84
+ exclude(...fields) {
85
+ for (const field of fields) {
86
+ this.projection[field] = 0;
87
+ }
88
+ return this;
89
+ }
90
+ orderBy(field, direction = "asc") {
91
+ this.sortSpec[field] = direction === "asc" || direction === 1 ? 1 : -1;
92
+ return this;
93
+ }
94
+ latest(field = "createdAt") {
95
+ return this.orderBy(field, "desc");
96
+ }
97
+ oldest(field = "createdAt") {
98
+ return this.orderBy(field, "asc");
99
+ }
100
+ limit(count) {
101
+ this.limitCount = count;
102
+ return this;
103
+ }
104
+ skip(count) {
105
+ this.skipCount = count;
106
+ return this;
107
+ }
108
+ offset(count) {
109
+ return this.skip(count);
110
+ }
111
+ async get() {
112
+ const cursor = this.nativeCollection.find(this.toFilter(), {
113
+ projection: Object.keys(this.projection).length > 0 ? this.projection : undefined,
114
+ sort: Object.keys(this.sortSpec).length > 0 ? this.sortSpec : undefined,
115
+ limit: this.limitCount,
116
+ skip: this.skipCount
117
+ });
118
+ return await cursor.toArray();
119
+ }
120
+ async first() {
121
+ const result = await this.nativeCollection.findOne(this.toFilter(), {
122
+ projection: Object.keys(this.projection).length > 0 ? this.projection : undefined,
123
+ sort: Object.keys(this.sortSpec).length > 0 ? this.sortSpec : undefined
124
+ });
125
+ return result;
126
+ }
127
+ async find(id) {
128
+ const { ObjectId } = await this.loadObjectId();
129
+ const result = await this.nativeCollection.findOne({ _id: new ObjectId(id) }, { projection: Object.keys(this.projection).length > 0 ? this.projection : undefined });
130
+ return result;
131
+ }
132
+ async count() {
133
+ return await this.nativeCollection.countDocuments(this.toFilter());
134
+ }
135
+ async exists() {
136
+ const count = await this.nativeCollection.countDocuments(this.toFilter(), { limit: 1 });
137
+ return count > 0;
138
+ }
139
+ async distinct(field) {
140
+ return await this.nativeCollection.distinct(field, this.toFilter());
141
+ }
142
+ async insert(document) {
143
+ const result = await this.nativeCollection.insertOne(document);
144
+ return {
145
+ insertedId: result.insertedId.toString(),
146
+ acknowledged: result.acknowledged
147
+ };
148
+ }
149
+ async insertMany(documents) {
150
+ const result = await this.nativeCollection.insertMany(documents);
151
+ return {
152
+ insertedIds: Object.values(result.insertedIds).map((id) => id.toString()),
153
+ insertedCount: result.insertedCount,
154
+ acknowledged: result.acknowledged
155
+ };
156
+ }
157
+ async update(update) {
158
+ const updateDoc = this.normalizeUpdate(update);
159
+ const result = await this.nativeCollection.updateOne(this.toFilter(), updateDoc);
160
+ return {
161
+ matchedCount: result.matchedCount,
162
+ modifiedCount: result.modifiedCount,
163
+ acknowledged: result.acknowledged,
164
+ ...result.upsertedId ? { upsertedId: result.upsertedId.toString() } : {}
165
+ };
166
+ }
167
+ async updateMany(update) {
168
+ const updateDoc = this.normalizeUpdate(update);
169
+ const result = await this.nativeCollection.updateMany(this.toFilter(), updateDoc);
170
+ return {
171
+ matchedCount: result.matchedCount,
172
+ modifiedCount: result.modifiedCount,
173
+ acknowledged: result.acknowledged,
174
+ ...result.upsertedId ? { upsertedId: result.upsertedId.toString() } : {}
175
+ };
176
+ }
177
+ async delete() {
178
+ const result = await this.nativeCollection.deleteOne(this.toFilter());
179
+ return {
180
+ deletedCount: result.deletedCount,
181
+ acknowledged: result.acknowledged
182
+ };
183
+ }
184
+ async deleteMany() {
185
+ const result = await this.nativeCollection.deleteMany(this.toFilter());
186
+ return {
187
+ deletedCount: result.deletedCount,
188
+ acknowledged: result.acknowledged
189
+ };
190
+ }
191
+ aggregate() {
192
+ return new MongoAggregateBuilder(this.nativeCollection, this.toFilter());
193
+ }
194
+ toFilter() {
195
+ if (this.orFilters.length === 0) {
196
+ return { ...this.filters };
197
+ }
198
+ return {
199
+ $or: [this.filters, ...this.orFilters]
200
+ };
201
+ }
202
+ clone() {
203
+ const cloned = new MongoQueryBuilder(this.nativeCollection, this.collectionName);
204
+ cloned.filters = { ...this.filters };
205
+ cloned.orFilters = [...this.orFilters];
206
+ cloned.projection = { ...this.projection };
207
+ cloned.sortSpec = { ...this.sortSpec };
208
+ cloned.limitCount = this.limitCount;
209
+ cloned.skipCount = this.skipCount;
210
+ return cloned;
211
+ }
212
+ mapOperator(operator, value) {
213
+ switch (operator) {
214
+ case "=":
215
+ return value;
216
+ case "!=":
217
+ return { $ne: value };
218
+ case ">":
219
+ return { $gt: value };
220
+ case ">=":
221
+ return { $gte: value };
222
+ case "<":
223
+ return { $lt: value };
224
+ case "<=":
225
+ return { $lte: value };
226
+ case "in":
227
+ return { $in: value };
228
+ case "nin":
229
+ return { $nin: value };
230
+ case "exists":
231
+ return { $exists: value };
232
+ case "regex":
233
+ return { $regex: value };
234
+ default:
235
+ return value;
236
+ }
237
+ }
238
+ normalizeUpdate(update) {
239
+ const hasOperator = Object.keys(update).some((key) => key.startsWith("$"));
240
+ if (hasOperator) {
241
+ return update;
242
+ }
243
+ return { $set: update };
244
+ }
245
+ async loadObjectId() {
246
+ const mongodb = await import("mongodb");
247
+ return mongodb;
248
+ }
249
+ }
250
+
251
+ class MongoAggregateBuilder {
252
+ nativeCollection;
253
+ pipeline = [];
254
+ constructor(nativeCollection, initialFilter) {
255
+ this.nativeCollection = nativeCollection;
256
+ if (initialFilter && Object.keys(initialFilter).length > 0) {
257
+ this.pipeline.push({ $match: initialFilter });
258
+ }
259
+ }
260
+ match(filter) {
261
+ this.pipeline.push({ $match: filter });
262
+ return this;
263
+ }
264
+ group(spec) {
265
+ this.pipeline.push({ $group: spec });
266
+ return this;
267
+ }
268
+ project(projection) {
269
+ this.pipeline.push({ $project: projection });
270
+ return this;
271
+ }
272
+ sort(spec) {
273
+ const normalizedSpec = {};
274
+ for (const [key, value] of Object.entries(spec)) {
275
+ normalizedSpec[key] = value === "asc" || value === 1 ? 1 : -1;
276
+ }
277
+ this.pipeline.push({ $sort: normalizedSpec });
278
+ return this;
279
+ }
280
+ limit(count) {
281
+ this.pipeline.push({ $limit: count });
282
+ return this;
283
+ }
284
+ skip(count) {
285
+ this.pipeline.push({ $skip: count });
286
+ return this;
287
+ }
288
+ unwind(field) {
289
+ this.pipeline.push({ $unwind: field });
290
+ return this;
291
+ }
292
+ lookup(options) {
293
+ this.pipeline.push({ $lookup: options });
294
+ return this;
295
+ }
296
+ addFields(fields) {
297
+ this.pipeline.push({ $addFields: fields });
298
+ return this;
299
+ }
300
+ count(fieldName) {
301
+ this.pipeline.push({ $count: fieldName });
302
+ return this;
303
+ }
304
+ async get() {
305
+ const cursor = this.nativeCollection.aggregate(this.pipeline);
306
+ return await cursor.toArray();
307
+ }
308
+ async first() {
309
+ this.pipeline.push({ $limit: 1 });
310
+ const results = await this.get();
311
+ return results[0] ?? null;
312
+ }
313
+ toPipeline() {
314
+ return [...this.pipeline];
315
+ }
316
+ }
317
+
318
+ // src/MongoClient.ts
319
+ class MongoClient {
320
+ config;
321
+ client = null;
322
+ db = null;
323
+ connected = false;
324
+ mongodb = null;
325
+ constructor(config = {}) {
326
+ this.config = config;
327
+ }
328
+ async connect() {
329
+ if (this.connected) {
330
+ return;
331
+ }
332
+ this.mongodb = await this.loadMongoDBModule();
333
+ const uri = this.buildConnectionUri();
334
+ const options = {
335
+ maxPoolSize: this.config.maxPoolSize ?? 10,
336
+ minPoolSize: this.config.minPoolSize ?? 1
337
+ };
338
+ if (this.config.connectTimeoutMS) {
339
+ options.connectTimeoutMS = this.config.connectTimeoutMS;
340
+ }
341
+ if (this.config.socketTimeoutMS) {
342
+ options.socketTimeoutMS = this.config.socketTimeoutMS;
343
+ }
344
+ this.client = new this.mongodb.MongoClient(uri, options);
345
+ await this.client.connect();
346
+ const dbName = this.config.database ?? "test";
347
+ this.db = this.client.db(dbName);
348
+ this.connected = true;
349
+ }
350
+ async disconnect() {
351
+ if (this.client) {
352
+ await this.client.close();
353
+ this.client = null;
354
+ this.db = null;
355
+ }
356
+ this.connected = false;
357
+ }
358
+ isConnected() {
359
+ return this.connected && this.client !== null;
360
+ }
361
+ database(name) {
362
+ const client = this.getClient();
363
+ const db = name ? client.db(name) : this.db;
364
+ return new MongoDatabaseWrapper(db);
365
+ }
366
+ collection(name) {
367
+ const db = this.getDatabase();
368
+ const nativeCollection = db.collection(name);
369
+ return new MongoQueryBuilder(nativeCollection, name);
370
+ }
371
+ buildConnectionUri() {
372
+ if (this.config.uri) {
373
+ return this.config.uri;
374
+ }
375
+ const host = this.config.host ?? "localhost";
376
+ const port = this.config.port ?? 27017;
377
+ const protocol = this.config.tls ? "mongodb+srv" : "mongodb";
378
+ let uri = `${protocol}://`;
379
+ if (this.config.username && this.config.password) {
380
+ uri += `${encodeURIComponent(this.config.username)}:${encodeURIComponent(this.config.password)}@`;
381
+ }
382
+ uri += `${host}`;
383
+ if (!this.config.tls) {
384
+ uri += `:${port}`;
385
+ }
386
+ if (this.config.database) {
387
+ uri += `/${this.config.database}`;
388
+ }
389
+ const params = [];
390
+ if (this.config.authSource) {
391
+ params.push(`authSource=${this.config.authSource}`);
392
+ }
393
+ if (this.config.replicaSet) {
394
+ params.push(`replicaSet=${this.config.replicaSet}`);
395
+ }
396
+ if (params.length > 0) {
397
+ uri += `?${params.join("&")}`;
398
+ }
399
+ return uri;
400
+ }
401
+ async loadMongoDBModule() {
402
+ try {
403
+ const mongodb = await import("mongodb");
404
+ return mongodb;
405
+ } catch {
406
+ throw new Error('MongoDB client requires the "mongodb" package. Please install it: bun add mongodb');
407
+ }
408
+ }
409
+ getClient() {
410
+ if (!this.client) {
411
+ throw new Error("MongoDB client not connected. Call connect() first.");
412
+ }
413
+ return this.client;
414
+ }
415
+ getDatabase() {
416
+ if (!this.db) {
417
+ throw new Error("MongoDB client not connected. Call connect() first.");
418
+ }
419
+ return this.db;
420
+ }
421
+ }
422
+
423
+ class MongoDatabaseWrapper {
424
+ db;
425
+ constructor(db) {
426
+ this.db = db;
427
+ }
428
+ collection(name) {
429
+ const nativeCollection = this.db.collection(name);
430
+ return new MongoQueryBuilder(nativeCollection, name);
431
+ }
432
+ async listCollections() {
433
+ const collections = await this.db.listCollections().toArray();
434
+ return collections.map((c) => c.name);
435
+ }
436
+ async dropCollection(name) {
437
+ return await this.db.dropCollection(name);
438
+ }
439
+ async createCollection(name) {
440
+ await this.db.createCollection(name);
441
+ }
442
+ }
443
+
444
+ // src/MongoManager.ts
445
+ class MongoManager {
446
+ connections = new Map;
447
+ defaultConnection = "default";
448
+ configs = new Map;
449
+ configure(config) {
450
+ this.defaultConnection = config.default ?? "default";
451
+ for (const [name, connectionConfig] of Object.entries(config.connections)) {
452
+ this.configs.set(name, connectionConfig);
453
+ }
454
+ }
455
+ addConnection(name, config) {
456
+ this.configs.set(name, config);
457
+ }
458
+ connection(name) {
459
+ const connectionName = name ?? this.defaultConnection;
460
+ if (!this.connections.has(connectionName)) {
461
+ const config = this.configs.get(connectionName);
462
+ if (!config) {
463
+ throw new Error(`MongoDB connection "${connectionName}" not configured`);
464
+ }
465
+ this.connections.set(connectionName, new MongoClient(config));
466
+ }
467
+ return this.connections.get(connectionName);
468
+ }
469
+ getDefault() {
470
+ return this.connection(this.defaultConnection);
471
+ }
472
+ async connectAll() {
473
+ for (const [name] of this.configs) {
474
+ const client = this.connection(name);
475
+ await client.connect();
476
+ }
477
+ }
478
+ async disconnectAll() {
479
+ for (const client of this.connections.values()) {
480
+ await client.disconnect();
481
+ }
482
+ this.connections.clear();
483
+ }
484
+ hasConnection(name) {
485
+ return this.configs.has(name);
486
+ }
487
+ async removeConnection(name) {
488
+ const client = this.connections.get(name);
489
+ if (client) {
490
+ await client.disconnect();
491
+ this.connections.delete(name);
492
+ }
493
+ this.configs.delete(name);
494
+ }
495
+ }
496
+
497
+ // src/Mongo.ts
498
+ var manager = new MongoManager;
499
+
500
+ class Mongo {
501
+ static configure(config) {
502
+ manager.configure(config);
503
+ }
504
+ static addConnection(name, config) {
505
+ manager.addConnection(name, config);
506
+ }
507
+ static connection(name) {
508
+ return manager.connection(name);
509
+ }
510
+ static async connect() {
511
+ await manager.getDefault().connect();
512
+ }
513
+ static async connectAll() {
514
+ await manager.connectAll();
515
+ }
516
+ static async disconnect() {
517
+ await manager.getDefault().disconnect();
518
+ }
519
+ static async disconnectAll() {
520
+ await manager.disconnectAll();
521
+ }
522
+ static isConnected() {
523
+ return manager.getDefault().isConnected();
524
+ }
525
+ static database(name) {
526
+ return manager.getDefault().database(name);
527
+ }
528
+ static collection(name) {
529
+ return manager.getDefault().collection(name);
530
+ }
531
+ }
532
+ export {
533
+ MongoQueryBuilder,
534
+ MongoManager,
535
+ MongoClient,
536
+ MongoAggregateBuilder,
537
+ Mongo
538
+ };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@gravito/dark-matter",
3
+ "version": "1.0.0-alpha.2",
4
+ "description": "MongoDB client for Gravito - Bun native, Laravel-style API",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "bun run build.ts",
17
+ "test": "bun test",
18
+ "typecheck": "bunx tsc --noEmit"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "keywords": [
25
+ "gravito",
26
+ "mongodb",
27
+ "nosql",
28
+ "bun",
29
+ "document-database"
30
+ ],
31
+ "author": "Gravito Team",
32
+ "license": "MIT",
33
+ "peerDependencies": {
34
+ "mongodb": "^6.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "mongodb": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "devDependencies": {
42
+ "typescript": "^5.0.0"
43
+ }
44
+ }