momos 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/index.cjs +375 -0
- package/dist/index.d.cts +573 -0
- package/dist/index.d.ts +573 -0
- package/dist/index.js +365 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rhinobase
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# 🥟 Momos
|
|
2
|
+
|
|
3
|
+
[](https://deepwiki.com/rhinobase/momos)
|
|
4
|
+
[](https://npmjs.org/package/momos "View this project on NPM")
|
|
5
|
+
[](https://www.npmjs.com/package/momos)
|
|
6
|
+
|
|
7
|
+
A lightweight, type-safe MongoDB ORM that uses your validation schema for type-safe queries, inserts, and updates. Validates documents before insert/update operations automatically.
|
|
8
|
+
|
|
9
|
+
This lib supports all the validation libs which are [Standard Schema](https://standardschema.dev/) compliant (zod, valibot, arktype, etc.).
|
|
10
|
+
|
|
11
|
+
For documentation visit [honohub.dev](https://honohub.dev/docs/momos).
|
|
12
|
+
|
|
13
|
+
> [!Note]
|
|
14
|
+
> This package is still in development and your feedback is highly appreciated. If you have any suggestions or issues, please let us know by creating an issue on GitHub.
|
|
15
|
+
|
|
16
|
+
## Contributing
|
|
17
|
+
|
|
18
|
+
Visit our [contributing docs](https://github.com/rhinobase/momos/blob/main/CONTRIBUTING.md).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class TypedCursor {
|
|
4
|
+
constructor(cursor) {
|
|
5
|
+
this.cursor = cursor;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Get the underlying MongoDB cursor
|
|
9
|
+
*/
|
|
10
|
+
get raw() {
|
|
11
|
+
return this.cursor;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns all documents as an array
|
|
15
|
+
*/
|
|
16
|
+
async toArray() {
|
|
17
|
+
return this.cursor.toArray();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Iterate over documents with a callback
|
|
21
|
+
*/
|
|
22
|
+
async forEach(callback) {
|
|
23
|
+
for await (const doc of this.cursor) {
|
|
24
|
+
await callback(doc);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Map documents to a new type
|
|
29
|
+
*/
|
|
30
|
+
map(transform) {
|
|
31
|
+
return new TypedCursor(this.cursor.map(transform));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if there are more documents
|
|
35
|
+
*/
|
|
36
|
+
async hasNext() {
|
|
37
|
+
return this.cursor.hasNext();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get the next document
|
|
41
|
+
*/
|
|
42
|
+
async next() {
|
|
43
|
+
return this.cursor.next();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Close the cursor
|
|
47
|
+
*/
|
|
48
|
+
async close() {
|
|
49
|
+
await this.cursor.close();
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Make the cursor iterable
|
|
53
|
+
*/
|
|
54
|
+
[Symbol.asyncIterator]() {
|
|
55
|
+
return this.cursor[Symbol.asyncIterator]();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
class TypedFindCursor extends TypedCursor {
|
|
59
|
+
/**
|
|
60
|
+
* Map documents to a new type
|
|
61
|
+
*/
|
|
62
|
+
map(transform) {
|
|
63
|
+
return new TypedFindCursor(this.cursor.map(transform));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Limit the number of documents returned
|
|
67
|
+
*/
|
|
68
|
+
limit(count) {
|
|
69
|
+
this.cursor.limit(count);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Skip a number of documents
|
|
74
|
+
*/
|
|
75
|
+
skip(count) {
|
|
76
|
+
this.cursor.skip(count);
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Sort the documents
|
|
81
|
+
*/
|
|
82
|
+
sort(sort) {
|
|
83
|
+
this.cursor.sort(sort);
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Project specific fields
|
|
88
|
+
*/
|
|
89
|
+
project(projection) {
|
|
90
|
+
this.cursor.project(projection);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Set batch size for cursor
|
|
95
|
+
*/
|
|
96
|
+
batchSize(size) {
|
|
97
|
+
this.cursor.batchSize(size);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
class TypedAggregationCursor extends TypedCursor {
|
|
102
|
+
/**
|
|
103
|
+
* Map documents to a new type
|
|
104
|
+
*/
|
|
105
|
+
map(transform) {
|
|
106
|
+
return new TypedAggregationCursor(this.cursor.map(transform));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class ValidationError extends Error {
|
|
111
|
+
issues;
|
|
112
|
+
constructor(issues) {
|
|
113
|
+
const message = issues.map((i) => i.message).join(", ");
|
|
114
|
+
super(`Validation failed: ${message}`);
|
|
115
|
+
this.name = "ValidationError";
|
|
116
|
+
this.issues = issues;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function validate(schema, data) {
|
|
121
|
+
const result = await schema["~standard"].validate(data);
|
|
122
|
+
if (result.issues) {
|
|
123
|
+
throw new ValidationError(result.issues);
|
|
124
|
+
}
|
|
125
|
+
return result.value;
|
|
126
|
+
}
|
|
127
|
+
async function validateMany(schema, data) {
|
|
128
|
+
return Promise.all(data.map((item) => validate(schema, item)));
|
|
129
|
+
}
|
|
130
|
+
async function isValid(schema, data) {
|
|
131
|
+
const result = await schema["~standard"].validate(data);
|
|
132
|
+
return !result.issues;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class TypedCollection {
|
|
136
|
+
collection;
|
|
137
|
+
schema;
|
|
138
|
+
shouldValidate;
|
|
139
|
+
constructor(collection, schema, options = {}) {
|
|
140
|
+
this.collection = collection;
|
|
141
|
+
this.schema = schema;
|
|
142
|
+
this.shouldValidate = options.validate !== false;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get the underlying MongoDB collection
|
|
146
|
+
*/
|
|
147
|
+
get raw() {
|
|
148
|
+
return this.collection;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get the collection name
|
|
152
|
+
*/
|
|
153
|
+
get collectionName() {
|
|
154
|
+
return this.collection.collectionName;
|
|
155
|
+
}
|
|
156
|
+
// ============================================
|
|
157
|
+
// INSERT OPERATIONS
|
|
158
|
+
// ============================================
|
|
159
|
+
/**
|
|
160
|
+
* Insert a single document
|
|
161
|
+
* Validates the document before insertion if validation is enabled
|
|
162
|
+
*/
|
|
163
|
+
async insertOne(doc, options) {
|
|
164
|
+
const validated = this.shouldValidate ? await validate(this.schema, doc) : doc;
|
|
165
|
+
return this.collection.insertOne(validated, options);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Insert multiple documents
|
|
169
|
+
* Validates all documents before insertion if validation is enabled
|
|
170
|
+
*/
|
|
171
|
+
async insertMany(docs, options) {
|
|
172
|
+
const validated = this.shouldValidate ? await validateMany(this.schema, docs) : docs;
|
|
173
|
+
return this.collection.insertMany(validated, options);
|
|
174
|
+
}
|
|
175
|
+
// ============================================
|
|
176
|
+
// FIND OPERATIONS
|
|
177
|
+
// ============================================
|
|
178
|
+
/**
|
|
179
|
+
* Find documents matching the filter
|
|
180
|
+
* Returns a typed cursor for further operations
|
|
181
|
+
*/
|
|
182
|
+
find(filter = {}, options) {
|
|
183
|
+
const cursor = this.collection.find(filter, options);
|
|
184
|
+
return new TypedFindCursor(cursor);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Find a single document matching the filter
|
|
188
|
+
*/
|
|
189
|
+
async findOne(filter = {}, options) {
|
|
190
|
+
const result = await this.collection.findOne(filter, options);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Find a document by its _id
|
|
195
|
+
*/
|
|
196
|
+
async findById(id, options) {
|
|
197
|
+
return this.findOne({ _id: id }, options);
|
|
198
|
+
}
|
|
199
|
+
// ============================================
|
|
200
|
+
// UPDATE OPERATIONS
|
|
201
|
+
// ============================================
|
|
202
|
+
/**
|
|
203
|
+
* Update a single document
|
|
204
|
+
*/
|
|
205
|
+
async updateOne(filter, update, options) {
|
|
206
|
+
return this.collection.updateOne(
|
|
207
|
+
filter,
|
|
208
|
+
update,
|
|
209
|
+
options
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Update multiple documents
|
|
214
|
+
*/
|
|
215
|
+
async updateMany(filter, update, options) {
|
|
216
|
+
return this.collection.updateMany(
|
|
217
|
+
filter,
|
|
218
|
+
update,
|
|
219
|
+
options
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Replace a single document
|
|
224
|
+
* Validates the replacement document if validation is enabled
|
|
225
|
+
*/
|
|
226
|
+
async replaceOne(filter, replacement, options) {
|
|
227
|
+
const validated = this.shouldValidate ? await validate(this.schema, replacement) : replacement;
|
|
228
|
+
return this.collection.replaceOne(
|
|
229
|
+
filter,
|
|
230
|
+
validated,
|
|
231
|
+
options
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Find a document and update it atomically
|
|
236
|
+
*/
|
|
237
|
+
async findOneAndUpdate(filter, update, options = {}) {
|
|
238
|
+
const result = await this.collection.findOneAndUpdate(
|
|
239
|
+
filter,
|
|
240
|
+
update,
|
|
241
|
+
options
|
|
242
|
+
);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Find a document and replace it atomically
|
|
247
|
+
* Validates the replacement document if validation is enabled
|
|
248
|
+
*/
|
|
249
|
+
async findOneAndReplace(filter, replacement, options = {}) {
|
|
250
|
+
const validated = this.shouldValidate ? await validate(this.schema, replacement) : replacement;
|
|
251
|
+
const result = await this.collection.findOneAndReplace(
|
|
252
|
+
filter,
|
|
253
|
+
validated,
|
|
254
|
+
options
|
|
255
|
+
);
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
// ============================================
|
|
259
|
+
// DELETE OPERATIONS
|
|
260
|
+
// ============================================
|
|
261
|
+
/**
|
|
262
|
+
* Delete a single document
|
|
263
|
+
*/
|
|
264
|
+
async deleteOne(filter, options) {
|
|
265
|
+
return this.collection.deleteOne(filter, options);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Delete multiple documents
|
|
269
|
+
*/
|
|
270
|
+
async deleteMany(filter, options) {
|
|
271
|
+
return this.collection.deleteMany(filter, options);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Find a document and delete it atomically
|
|
275
|
+
*/
|
|
276
|
+
async findOneAndDelete(filter, options = {}) {
|
|
277
|
+
const result = await this.collection.findOneAndDelete(
|
|
278
|
+
filter,
|
|
279
|
+
options
|
|
280
|
+
);
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
// ============================================
|
|
284
|
+
// COUNT OPERATIONS
|
|
285
|
+
// ============================================
|
|
286
|
+
/**
|
|
287
|
+
* Count documents matching the filter
|
|
288
|
+
*/
|
|
289
|
+
async countDocuments(filter = {}, options) {
|
|
290
|
+
return this.collection.countDocuments(filter, options);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get an estimated count of documents (faster, uses metadata)
|
|
294
|
+
*/
|
|
295
|
+
async estimatedDocumentCount(options) {
|
|
296
|
+
return this.collection.estimatedDocumentCount(options);
|
|
297
|
+
}
|
|
298
|
+
// ============================================
|
|
299
|
+
// AGGREGATION
|
|
300
|
+
// ============================================
|
|
301
|
+
/**
|
|
302
|
+
* Run an aggregation pipeline
|
|
303
|
+
*/
|
|
304
|
+
aggregate(pipeline, options) {
|
|
305
|
+
const cursor = this.collection.aggregate(pipeline, options);
|
|
306
|
+
return new TypedAggregationCursor(cursor);
|
|
307
|
+
}
|
|
308
|
+
// ============================================
|
|
309
|
+
// INDEX OPERATIONS
|
|
310
|
+
// ============================================
|
|
311
|
+
/**
|
|
312
|
+
* Create an index
|
|
313
|
+
*/
|
|
314
|
+
async createIndex(indexSpec, options) {
|
|
315
|
+
return this.collection.createIndex(indexSpec, options);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Create multiple indexes
|
|
319
|
+
*/
|
|
320
|
+
async createIndexes(indexSpecs, options) {
|
|
321
|
+
return this.collection.createIndexes(indexSpecs, options);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Drop an index
|
|
325
|
+
*/
|
|
326
|
+
async dropIndex(indexName, options) {
|
|
327
|
+
await this.collection.dropIndex(indexName, options);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* List all indexes
|
|
331
|
+
*/
|
|
332
|
+
async indexes() {
|
|
333
|
+
return this.collection.indexes();
|
|
334
|
+
}
|
|
335
|
+
// ============================================
|
|
336
|
+
// UTILITY OPERATIONS
|
|
337
|
+
// ============================================
|
|
338
|
+
/**
|
|
339
|
+
* Get distinct values for a field
|
|
340
|
+
*/
|
|
341
|
+
async distinct(field, filter = {}) {
|
|
342
|
+
const result = await this.collection.distinct(
|
|
343
|
+
field,
|
|
344
|
+
filter
|
|
345
|
+
);
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Check if a document exists
|
|
350
|
+
*/
|
|
351
|
+
async exists(filter) {
|
|
352
|
+
const count = await this.countDocuments(filter, { limit: 1 });
|
|
353
|
+
return count > 0;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Drop the collection
|
|
357
|
+
*/
|
|
358
|
+
async drop() {
|
|
359
|
+
await this.collection.drop();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function defineCollection(db, name, schema, options = {}) {
|
|
363
|
+
const collection = db.collection(name);
|
|
364
|
+
return new TypedCollection(collection, schema, options);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
exports.TypedAggregationCursor = TypedAggregationCursor;
|
|
368
|
+
exports.TypedCollection = TypedCollection;
|
|
369
|
+
exports.TypedCursor = TypedCursor;
|
|
370
|
+
exports.TypedFindCursor = TypedFindCursor;
|
|
371
|
+
exports.ValidationError = ValidationError;
|
|
372
|
+
exports.defineCollection = defineCollection;
|
|
373
|
+
exports.isValid = isValid;
|
|
374
|
+
exports.validate = validate;
|
|
375
|
+
exports.validateMany = validateMany;
|