Thin MongoDB ODM built for Standard Schema
mongodb zod deno

denoify

.DS_Store

This is a binary file and will not be displayed.

+1 -1
.gitignore
··· 1 - node_modules/ 1 + .DS_Store
-2
.npmignore
··· 1 - node_modules/ 2 - tests/
+47 -33
README.md
··· 1 - # **mizzleORM** 1 + # **Nozzle** 2 2 3 - A lightweight, type-safe ODM for MongoDB in TypeScript — inspired by [Drizzle ORM](https://orm.drizzle.team/) and built for developers who value simplicity, transparency, and strong typings. 4 - 3 + A lightweight, type-safe ODM for MongoDB in TypeScript — inspired by 4 + [Drizzle ORM](https://orm.drizzle.team/) and built for developers who value 5 + simplicity, transparency, and strong typings. 5 6 6 - > **Note:** MizzleORM requires MongoDB **4.2 or newer** and works best with the latest stable MongoDB server (6.x or newer) and the official [mongodb](https://www.npmjs.com/package/mongodb) Node.js driver (v6+). 7 + > **Note:** Nozzle DB requires MongoDB **4.2 or newer** and works best with the 8 + > latest stable MongoDB server (6.x or newer) and the official 9 + > [mongodb](https://www.npmjs.com/package/mongodb) Node.js driver (v6+). 7 10 8 11 ## ✨ Features 9 12 10 - * **Schema-first:** Define and validate collections using [Zod](https://zod.dev/). 11 - * **Type-safe operations:** Auto-complete and strict typings for `insert`, `find`, `update`, and `delete`. 12 - * **Minimal & modular:** No decorators or magic. Just clean, composable APIs. 13 - * **Developer-friendly DX:** Great TypeScript support and IDE integration. 14 - * **Built on MongoDB native driver:** Zero overhead with full control. 13 + - **Schema-first:** Define and validate collections using 14 + [Zod](https://zod.dev/). 15 + - **Type-safe operations:** Auto-complete and strict typings for `insert`, 16 + `find`, `update`, and `delete`. 17 + - **Minimal & modular:** No decorators or magic. Just clean, composable APIs. 18 + - **Developer-friendly DX:** Great TypeScript support and IDE integration. 19 + - **Built on MongoDB native driver:** Zero overhead with full control. 15 20 16 21 --- 17 22 ··· 23 28 yarn add mizzleorm mongodb zod 24 29 ``` 25 30 26 - > If you need to upgrade your local MongoDB server, see: https://www.mongodb.com/docs/manual/administration/install-community/ 31 + > If you need to upgrade your local MongoDB server, see: 32 + > https://www.mongodb.com/docs/manual/administration/install-community/ 27 33 28 34 --- 29 35 ··· 33 39 34 40 ```ts 35 41 // src/schemas/user.ts 36 - import { z } from 'zod'; 37 - import { defineModel } from 'mizzleorm'; 42 + import { z } from "zod"; 43 + import { defineModel } from "mizzleorm"; 38 44 39 45 export const userSchema = defineModel(z.object({ 40 46 name: z.string(), ··· 52 58 53 59 ```ts 54 60 // src/index.ts 55 - import { connect, disconnect, MongoModel, InferModel, InsertType } from 'mizzleorm'; 56 - import { userSchema } from './schemas/user'; 57 - import { ObjectId } from 'mongodb'; // v6+ driver recommended 61 + import { 62 + connect, 63 + disconnect, 64 + InferModel, 65 + InsertType, 66 + MongoModel, 67 + } from "mizzleorm"; 68 + import { userSchema } from "./schemas/user"; 69 + import { ObjectId } from "mongodb"; // v6+ driver recommended 58 70 59 71 type User = InferModel<typeof userSchema>; 60 72 type UserInsert = InsertType<typeof userSchema>; 61 73 62 74 async function main() { 63 75 // Use the latest connection string format and options 64 - await connect('mongodb://localhost:27017', 'your_database_name'); 65 - const UserModel = new MongoModel('users', userSchema); 76 + await connect("mongodb://localhost:27017", "your_database_name"); 77 + const UserModel = new MongoModel("users", userSchema); 66 78 67 79 // Your operations go here 68 80 ··· 79 91 ```ts 80 92 // Insert one 81 93 const newUser: UserInsert = { 82 - name: 'John Doe', 83 - email: 'john.doe@example.com', 94 + name: "John Doe", 95 + email: "john.doe@example.com", 84 96 age: 30, 85 97 }; 86 98 const insertResult = await UserModel.insertOne(newUser); 87 99 88 100 // Find many 89 - const users = await UserModel.find({ name: 'John Doe' }); 101 + const users = await UserModel.find({ name: "John Doe" }); 90 102 91 103 // Find one 92 - const found = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); // ObjectId from mongodb v6+ 104 + const found = await UserModel.findOne({ 105 + _id: new ObjectId(insertResult.insertedId), 106 + }); // ObjectId from mongodb v6+ 93 107 94 108 // Update 95 - await UserModel.update({ name: 'John Doe' }, { age: 31 }); 109 + await UserModel.update({ name: "John Doe" }, { age: 31 }); 96 110 97 111 // Delete 98 - await UserModel.delete({ name: 'John Doe' }); 112 + await UserModel.delete({ name: "John Doe" }); 99 113 100 114 // Insert many 101 115 await UserModel.insertMany([ 102 - { name: 'Alice', email: 'alice@example.com', age: 25 }, 103 - { name: 'Bob', email: 'bob@example.com' }, 116 + { name: "Alice", email: "alice@example.com", age: 25 }, 117 + { name: "Bob", email: "bob@example.com" }, 104 118 ]); 105 119 106 120 // Find by ID 107 121 await UserModel.findById(insertResult.insertedId); 108 122 109 123 // Update one 110 - await UserModel.updateOne({ name: 'Alice' }, { age: 26 }); 124 + await UserModel.updateOne({ name: "Alice" }, { age: 26 }); 111 125 112 126 // Replace one 113 - await UserModel.replaceOne({ name: 'Bob' }, { 114 - name: 'Bob', 115 - email: 'bob@newmail.com', 127 + await UserModel.replaceOne({ name: "Bob" }, { 128 + name: "Bob", 129 + email: "bob@newmail.com", 116 130 age: 22, 117 131 }); 118 132 119 133 // Delete one 120 - await UserModel.deleteOne({ name: 'Alice' }); 134 + await UserModel.deleteOne({ name: "Alice" }); 121 135 122 136 // Count 123 137 const count = await UserModel.count({ age: { $gte: 18 } }); ··· 125 139 // Aggregation 126 140 const aggregation = await UserModel.aggregate([ 127 141 { $match: { age: { $gte: 18 } } }, 128 - { $group: { _id: null, avgAge: { $avg: '$age' } } }, 142 + { $group: { _id: null, avgAge: { $avg: "$age" } } }, 129 143 ]); 130 144 131 145 // Paginated query 132 146 const paginated = await UserModel.findPaginated( 133 147 { age: { $gte: 18 } }, 134 - { skip: 0, limit: 10, sort: { age: -1 } } 148 + { skip: 0, limit: 10, sort: { age: -1 } }, 135 149 ); 136 150 ``` 137 151 ··· 176 190 177 191 MIT — use it freely and contribute back if you'd like! 178 192 179 - --- 193 + ---
+18
deno.json
··· 1 + { 2 + "name": "mizzleorm", 3 + "version": "0.1.0", 4 + "exports": "./mod.ts", 5 + "license": "MIT", 6 + "tasks": { 7 + "build": "tsc", 8 + "test": "deno run --allow-run --allow-read scripts/test.ts --all", 9 + "test:mock": "deno test tests/mock_test.ts", 10 + "test:integration": "deno test --allow-net --allow-read --allow-write --allow-env --allow-sys tests/main_test.ts", 11 + "test:watch": "deno run --allow-run --allow-read scripts/test.ts --mock --watch", 12 + "example": "ts-node examples/user.ts" 13 + }, 14 + "imports": { 15 + "zod": "jsr:@zod/zod@^4.0.17", 16 + "mongodb": "npm:mongodb@^6.18.0" 17 + } 18 + }
+112
deno.lock
··· 1 + { 2 + "version": "5", 3 + "specifiers": { 4 + "jsr:@std/assert@*": "1.0.13", 5 + "jsr:@std/assert@^1.0.13": "1.0.13", 6 + "jsr:@std/internal@^1.0.10": "1.0.10", 7 + "jsr:@std/internal@^1.0.6": "1.0.10", 8 + "jsr:@std/testing@*": "1.0.15", 9 + "jsr:@zod/zod@^4.0.17": "4.0.17", 10 + "npm:@types/node@*": "22.15.15", 11 + "npm:mongodb@^6.18.0": "6.18.0" 12 + }, 13 + "jsr": { 14 + "@std/assert@1.0.13": { 15 + "integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29", 16 + "dependencies": [ 17 + "jsr:@std/internal@^1.0.6" 18 + ] 19 + }, 20 + "@std/internal@1.0.10": { 21 + "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" 22 + }, 23 + "@std/testing@1.0.15": { 24 + "integrity": "a490169f5ccb0f3ae9c94fbc69d2cd43603f2cffb41713a85f99bbb0e3087cbc", 25 + "dependencies": [ 26 + "jsr:@std/assert@^1.0.13", 27 + "jsr:@std/internal@^1.0.10" 28 + ] 29 + }, 30 + "@zod/zod@4.0.17": { 31 + "integrity": "4d9be90a1a3c16e09dad7ce25986379d7ab8ed5f5f843288509af6bf8def525f" 32 + } 33 + }, 34 + "npm": { 35 + "@mongodb-js/saslprep@1.3.0": { 36 + "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", 37 + "dependencies": [ 38 + "sparse-bitfield" 39 + ] 40 + }, 41 + "@types/node@22.15.15": { 42 + "integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==", 43 + "dependencies": [ 44 + "undici-types" 45 + ] 46 + }, 47 + "@types/webidl-conversions@7.0.3": { 48 + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" 49 + }, 50 + "@types/whatwg-url@11.0.5": { 51 + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", 52 + "dependencies": [ 53 + "@types/webidl-conversions" 54 + ] 55 + }, 56 + "bson@6.10.4": { 57 + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==" 58 + }, 59 + "memory-pager@1.5.0": { 60 + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" 61 + }, 62 + "mongodb-connection-string-url@3.0.2": { 63 + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", 64 + "dependencies": [ 65 + "@types/whatwg-url", 66 + "whatwg-url" 67 + ] 68 + }, 69 + "mongodb@6.18.0": { 70 + "integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==", 71 + "dependencies": [ 72 + "@mongodb-js/saslprep", 73 + "bson", 74 + "mongodb-connection-string-url" 75 + ] 76 + }, 77 + "punycode@2.3.1": { 78 + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" 79 + }, 80 + "sparse-bitfield@3.0.3": { 81 + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 82 + "dependencies": [ 83 + "memory-pager" 84 + ] 85 + }, 86 + "tr46@5.1.1": { 87 + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", 88 + "dependencies": [ 89 + "punycode" 90 + ] 91 + }, 92 + "undici-types@6.21.0": { 93 + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" 94 + }, 95 + "webidl-conversions@7.0.0": { 96 + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" 97 + }, 98 + "whatwg-url@14.2.0": { 99 + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", 100 + "dependencies": [ 101 + "tr46", 102 + "webidl-conversions" 103 + ] 104 + } 105 + }, 106 + "workspace": { 107 + "dependencies": [ 108 + "jsr:@zod/zod@^4.0.17", 109 + "npm:mongodb@^6.18.0" 110 + ] 111 + } 112 + }
+220 -97
examples/user.js
··· 1 1 "use strict"; 2 - var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 2 + var __awaiter = (this && this.__awaiter) || 3 + function (thisArg, _arguments, P, generator) { 4 + function adopt(value) { 5 + return value instanceof P ? value : new P(function (resolve) { 6 + resolve(value); 7 + }); 8 + } 4 9 return new (P || (P = Promise))(function (resolve, reject) { 5 - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 - step((generator = generator.apply(thisArg, _arguments || [])).next()); 10 + function fulfilled(value) { 11 + try { 12 + step(generator.next(value)); 13 + } catch (e) { 14 + reject(e); 15 + } 16 + } 17 + function rejected(value) { 18 + try { 19 + step(generator["throw"](value)); 20 + } catch (e) { 21 + reject(e); 22 + } 23 + } 24 + function step(result) { 25 + result.done 26 + ? resolve(result.value) 27 + : adopt(result.value).then(fulfilled, rejected); 28 + } 29 + step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 30 }); 10 - }; 31 + }; 11 32 var __generator = (this && this.__generator) || function (thisArg, body) { 12 - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); 13 - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 - function verb(n) { return function (v) { return step([n, v]); }; } 15 - function step(op) { 16 - if (f) throw new TypeError("Generator is already executing."); 17 - while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 - if (y = 0, t) op = [op[0] & 2, t.value]; 20 - switch (op[0]) { 21 - case 0: case 1: t = op; break; 22 - case 4: _.label++; return { value: op[1], done: false }; 23 - case 5: _.label++; y = op[1]; op = [0]; continue; 24 - case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 - default: 26 - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 - if (t[2]) _.ops.pop(); 31 - _.trys.pop(); continue; 33 + var _ = { 34 + label: 0, 35 + sent: function () { 36 + if (t[0] & 1) throw t[1]; 37 + return t[1]; 38 + }, 39 + trys: [], 40 + ops: [], 41 + }, 42 + f, 43 + y, 44 + t, 45 + g = Object.create( 46 + (typeof Iterator === "function" ? Iterator : Object).prototype, 47 + ); 48 + return g.next = verb(0), 49 + g["throw"] = verb(1), 50 + g["return"] = verb(2), 51 + typeof Symbol === "function" && (g[Symbol.iterator] = function () { 52 + return this; 53 + }), 54 + g; 55 + function verb(n) { 56 + return function (v) { 57 + return step([n, v]); 58 + }; 59 + } 60 + function step(op) { 61 + if (f) throw new TypeError("Generator is already executing."); 62 + while (g && (g = 0, op[0] && (_ = 0)), _) { 63 + try { 64 + if ( 65 + f = 1, 66 + y && (t = op[0] & 2 67 + ? y["return"] 68 + : op[0] 69 + ? y["throw"] || ((t = y["return"]) && t.call(y), 0) 70 + : y.next) && 71 + !(t = t.call(y, op[1])).done 72 + ) return t; 73 + if (y = 0, t) op = [op[0] & 2, t.value]; 74 + switch (op[0]) { 75 + case 0: 76 + case 1: 77 + t = op; 78 + break; 79 + case 4: 80 + _.label++; 81 + return { value: op[1], done: false }; 82 + case 5: 83 + _.label++; 84 + y = op[1]; 85 + op = [0]; 86 + continue; 87 + case 7: 88 + op = _.ops.pop(); 89 + _.trys.pop(); 90 + continue; 91 + default: 92 + if ( 93 + !(t = _.trys, t = t.length > 0 && t[t.length - 1]) && 94 + (op[0] === 6 || op[0] === 2) 95 + ) { 96 + _ = 0; 97 + continue; 98 + } 99 + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { 100 + _.label = op[1]; 101 + break; 102 + } 103 + if (op[0] === 6 && _.label < t[1]) { 104 + _.label = t[1]; 105 + t = op; 106 + break; 107 + } 108 + if (t && _.label < t[2]) { 109 + _.label = t[2]; 110 + _.ops.push(op); 111 + break; 32 112 } 33 - op = body.call(thisArg, _); 34 - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 113 + if (t[2]) _.ops.pop(); 114 + _.trys.pop(); 115 + continue; 116 + } 117 + op = body.call(thisArg, _); 118 + } catch (e) { 119 + op = [6, e]; 120 + y = 0; 121 + } finally { 122 + f = t = 0; 123 + } 36 124 } 125 + if (op[0] & 5) throw op[1]; 126 + return { value: op[0] ? op[1] : void 0, done: true }; 127 + } 37 128 }; 38 129 Object.defineProperty(exports, "__esModule", { value: true }); 39 130 var zod_1 = require("zod"); ··· 41 132 var mongodb_1 = require("mongodb"); 42 133 // 1. Define your schema using Zod 43 134 var userSchema = (0, src_1.defineModel)(zod_1.z.object({ 44 - name: zod_1.z.string(), 45 - email: zod_1.z.string().email(), 46 - age: zod_1.z.number().int().positive().optional(), 47 - createdAt: zod_1.z.date().default(function () { return new Date(); }), 135 + name: zod_1.z.string(), 136 + email: zod_1.z.string().email(), 137 + age: zod_1.z.number().int().positive().optional(), 138 + createdAt: zod_1.z.date().default(function () { 139 + return new Date(); 140 + }), 48 141 })); 49 142 function runExample() { 50 - return __awaiter(this, void 0, void 0, function () { 51 - var UserModel, newUser, insertResult, users, foundUser, updateResult, updatedUser, deleteResult, error_1; 52 - return __generator(this, function (_a) { 53 - switch (_a.label) { 54 - case 0: 55 - _a.trys.push([0, 9, 10, 12]); 56 - // 3. Connect to MongoDB 57 - return [4 /*yield*/, (0, src_1.connect)('mongodb://localhost:27017', 'mizzleorm_example')]; 58 - case 1: 59 - // 3. Connect to MongoDB 60 - _a.sent(); 61 - console.log('Connected to MongoDB'); 62 - UserModel = new src_1.MongoModel('users', userSchema); 63 - // Clean up previous data 64 - return [4 /*yield*/, UserModel.delete({})]; 65 - case 2: 66 - // Clean up previous data 67 - _a.sent(); 68 - newUser = { 69 - name: 'Alice Smith', 70 - email: 'alice@example.com', 71 - age: 30, 72 - }; 73 - return [4 /*yield*/, UserModel.insertOne(newUser)]; 74 - case 3: 75 - insertResult = _a.sent(); 76 - console.log('Inserted user:', insertResult.insertedId); 77 - return [4 /*yield*/, UserModel.find({ name: 'Alice Smith' })]; 78 - case 4: 79 - users = _a.sent(); 80 - console.log('Found users:', users); 81 - return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 82 - case 5: 83 - foundUser = _a.sent(); 84 - console.log('Found one user:', foundUser); 85 - return [4 /*yield*/, UserModel.update({ _id: new mongodb_1.ObjectId(insertResult.insertedId) }, { age: 31 })]; 86 - case 6: 87 - updateResult = _a.sent(); 88 - console.log('Updated user count:', updateResult.modifiedCount); 89 - return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 90 - case 7: 91 - updatedUser = _a.sent(); 92 - console.log('Updated user data:', updatedUser); 93 - return [4 /*yield*/, UserModel.delete({ name: 'Alice Smith' })]; 94 - case 8: 95 - deleteResult = _a.sent(); 96 - console.log('Deleted user count:', deleteResult.deletedCount); 97 - return [3 /*break*/, 12]; 98 - case 9: 99 - error_1 = _a.sent(); 100 - console.error('Error during example run:', error_1); 101 - return [3 /*break*/, 12]; 102 - case 10: 103 - // 9. Disconnect from MongoDB 104 - return [4 /*yield*/, (0, src_1.disconnect)()]; 105 - case 11: 106 - // 9. Disconnect from MongoDB 107 - _a.sent(); 108 - console.log('Disconnected from MongoDB'); 109 - return [7 /*endfinally*/]; 110 - case 12: return [2 /*return*/]; 111 - } 112 - }); 143 + return __awaiter(this, void 0, void 0, function () { 144 + var UserModel, 145 + newUser, 146 + insertResult, 147 + users, 148 + foundUser, 149 + updateResult, 150 + updatedUser, 151 + deleteResult, 152 + error_1; 153 + return __generator(this, function (_a) { 154 + switch (_a.label) { 155 + case 0: 156 + _a.trys.push([0, 9, 10, 12]); 157 + // 3. Connect to MongoDB 158 + return [ 159 + 4, /*yield*/ 160 + (0, src_1.connect)( 161 + "mongodb://localhost:27017", 162 + "mizzleorm_example", 163 + ), 164 + ]; 165 + case 1: 166 + // 3. Connect to MongoDB 167 + _a.sent(); 168 + console.log("Connected to MongoDB"); 169 + UserModel = new src_1.MongoModel("users", userSchema); 170 + // Clean up previous data 171 + return [4, /*yield*/ UserModel.delete({})]; 172 + case 2: 173 + // Clean up previous data 174 + _a.sent(); 175 + newUser = { 176 + name: "Alice Smith", 177 + email: "alice@example.com", 178 + age: 30, 179 + }; 180 + return [4, /*yield*/ UserModel.insertOne(newUser)]; 181 + case 3: 182 + insertResult = _a.sent(); 183 + console.log("Inserted user:", insertResult.insertedId); 184 + return [4, /*yield*/ UserModel.find({ name: "Alice Smith" })]; 185 + case 4: 186 + users = _a.sent(); 187 + console.log("Found users:", users); 188 + return [ 189 + 4, /*yield*/ 190 + UserModel.findOne({ 191 + _id: new mongodb_1.ObjectId(insertResult.insertedId), 192 + }), 193 + ]; 194 + case 5: 195 + foundUser = _a.sent(); 196 + console.log("Found one user:", foundUser); 197 + return [ 198 + 4, /*yield*/ 199 + UserModel.update({ 200 + _id: new mongodb_1.ObjectId(insertResult.insertedId), 201 + }, { age: 31 }), 202 + ]; 203 + case 6: 204 + updateResult = _a.sent(); 205 + console.log("Updated user count:", updateResult.modifiedCount); 206 + return [ 207 + 4, /*yield*/ 208 + UserModel.findOne({ 209 + _id: new mongodb_1.ObjectId(insertResult.insertedId), 210 + }), 211 + ]; 212 + case 7: 213 + updatedUser = _a.sent(); 214 + console.log("Updated user data:", updatedUser); 215 + return [4, /*yield*/ UserModel.delete({ name: "Alice Smith" })]; 216 + case 8: 217 + deleteResult = _a.sent(); 218 + console.log("Deleted user count:", deleteResult.deletedCount); 219 + return [3, /*break*/ 12]; 220 + case 9: 221 + error_1 = _a.sent(); 222 + console.error("Error during example run:", error_1); 223 + return [3, /*break*/ 12]; 224 + case 10: 225 + // 9. Disconnect from MongoDB 226 + return [4, /*yield*/ (0, src_1.disconnect)()]; 227 + case 11: 228 + // 9. Disconnect from MongoDB 229 + _a.sent(); 230 + console.log("Disconnected from MongoDB"); 231 + return [7 /*endfinally*/]; 232 + case 12: 233 + return [2 /*return*/]; 234 + } 113 235 }); 236 + }); 114 237 } 115 238 runExample();
+32 -26
examples/user.ts
··· 1 - import { z } from 'zod'; 2 - import { defineModel, MongoModel, connect, disconnect, InferModel, InsertType } from '../src'; 3 - import { ObjectId } from 'mongodb'; 1 + import { z } from "zod"; 2 + import { 3 + connect, 4 + defineModel, 5 + disconnect, 6 + InferModel, 7 + InsertType, 8 + MongoModel, 9 + } from "../src"; 10 + import { ObjectId } from "mongodb"; 4 11 5 12 // 1. Define your schema using Zod 6 13 const userSchema = defineModel(z.object({ ··· 17 24 async function runExample() { 18 25 try { 19 26 // 3. Connect to MongoDB 20 - await connect('mongodb://localhost:27017', 'mizzleorm_example'); 21 - console.log('Connected to MongoDB'); 27 + await connect("mongodb://localhost:27017", "mizzleorm_example"); 28 + console.log("Connected to MongoDB"); 22 29 23 30 // 2. Create a MongoModel for your collection 24 - const UserModel = new MongoModel('users', userSchema); 31 + const UserModel = new MongoModel("users", userSchema); 25 32 26 33 // Clean up previous data 27 34 await UserModel.delete({}); 28 35 29 36 // 4. Insert a new document 30 37 const newUser: UserInsert = { 31 - name: 'Alice Smith', 32 - email: 'alice@example.com', 38 + name: "Alice Smith", 39 + email: "alice@example.com", 33 40 age: 30, 34 41 }; 35 42 const insertResult = await UserModel.insertOne(newUser); 36 - console.log('Inserted user:', insertResult.insertedId); 43 + console.log("Inserted user:", insertResult.insertedId); 37 44 38 45 // 5. Find documents 39 - const users = await UserModel.find({ name: 'Alice Smith' }); 40 - console.log('Found users:', users); 46 + const users = await UserModel.find({ name: "Alice Smith" }); 47 + console.log("Found users:", users); 41 48 42 49 // 6. Find one document 43 - const foundUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); 44 - console.log('Found one user:', foundUser); 50 + const foundUser = await UserModel.findOne({ 51 + _id: new ObjectId(insertResult.insertedId), 52 + }); 53 + console.log("Found one user:", foundUser); 45 54 46 55 // 7. Update a document 47 56 const updateResult = await UserModel.update( 48 57 { _id: new ObjectId(insertResult.insertedId) }, 49 - { age: 31 } 58 + { age: 31 }, 50 59 ); 51 - console.log('Updated user count:', updateResult.modifiedCount); 60 + console.log("Updated user count:", updateResult.modifiedCount); 52 61 53 - const updatedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); 54 - console.log('Updated user data:', updatedUser); 62 + const updatedUser = await UserModel.findOne({ 63 + _id: new ObjectId(insertResult.insertedId), 64 + }); 65 + console.log("Updated user data:", updatedUser); 55 66 56 67 // 8. Delete documents 57 - const deleteResult = await UserModel.delete({ name: 'Alice Smith' }); 58 - console.log('Deleted user count:', deleteResult.deletedCount); 59 - 68 + const deleteResult = await UserModel.delete({ name: "Alice Smith" }); 69 + console.log("Deleted user count:", deleteResult.deletedCount); 60 70 } catch (error) { 61 - console.error('Error during example run:', error); 71 + console.error("Error during example run:", error); 62 72 } finally { 63 73 // 9. Disconnect from MongoDB 64 74 await disconnect(); 65 - console.log('Disconnected from MongoDB'); 75 + console.log("Disconnected from MongoDB"); 66 76 } 67 77 } 68 78 69 79 runExample(); 70 - 71 - 72 - 73 -
+3
mod.ts
··· 1 + export { defineModel, type InferModel, type InsertType } from "./schema.ts"; 2 + export { connect, disconnect } from "./client.ts"; 3 + export { Model } from "./model.ts";
+108
model.ts
··· 1 + import type { z } from "zod"; 2 + import type { 3 + Collection, 4 + DeleteResult, 5 + Document, 6 + Filter, 7 + InsertManyResult, 8 + InsertOneResult, 9 + OptionalUnlessRequiredId, 10 + UpdateResult, 11 + WithId, 12 + } from "mongodb"; 13 + import { ObjectId } from "mongodb"; 14 + import { getDb } from "./client.ts"; 15 + import type { InsertType } from "./schema.ts"; 16 + 17 + export class Model<T extends z.ZodObject> { 18 + private collection: Collection<z.infer<T>>; 19 + private schema: T; 20 + 21 + constructor(collectionName: string, schema: T) { 22 + this.collection = getDb().collection<z.infer<T>>(collectionName); 23 + this.schema = schema; 24 + } 25 + 26 + async insertOne(data: InsertType<T>): Promise<InsertOneResult<z.infer<T>>> { 27 + const validatedData = this.schema.parse(data); 28 + return await this.collection.insertOne( 29 + validatedData as OptionalUnlessRequiredId<z.infer<T>>, 30 + ); 31 + } 32 + 33 + async insertMany( 34 + data: InsertType<T>[], 35 + ): Promise<InsertManyResult<z.infer<T>>> { 36 + const validatedData = data.map((item) => this.schema.parse(item)); 37 + return await this.collection.insertMany( 38 + validatedData as OptionalUnlessRequiredId<z.infer<T>>[], 39 + ); 40 + } 41 + 42 + async find(query: Filter<z.infer<T>>): Promise<(WithId<z.infer<T>>)[]> { 43 + return await this.collection.find(query).toArray(); 44 + } 45 + 46 + async findOne(query: Filter<z.infer<T>>): Promise<WithId<z.infer<T>> | null> { 47 + return await this.collection.findOne(query); 48 + } 49 + 50 + async findById(id: string | ObjectId): Promise<WithId<z.infer<T>> | null> { 51 + const objectId = typeof id === "string" ? new ObjectId(id) : id; 52 + return await this.findOne({ _id: objectId } as Filter<z.infer<T>>); 53 + } 54 + 55 + async update( 56 + query: Filter<z.infer<T>>, 57 + data: Partial<z.infer<T>>, 58 + ): Promise<UpdateResult> { 59 + return await this.collection.updateMany(query, { $set: data }); 60 + } 61 + 62 + async updateOne( 63 + query: Filter<z.infer<T>>, 64 + data: Partial<z.infer<T>>, 65 + ): Promise<UpdateResult> { 66 + return await this.collection.updateOne(query, { $set: data }); 67 + } 68 + 69 + async replaceOne( 70 + query: Filter<z.infer<T>>, 71 + data: InsertType<T>, 72 + ): Promise<UpdateResult> { 73 + const validatedData = this.schema.parse(data); 74 + return await this.collection.replaceOne( 75 + query, 76 + validatedData as OptionalUnlessRequiredId<z.infer<T>>, 77 + ); 78 + } 79 + 80 + async delete(query: Filter<z.infer<T>>): Promise<DeleteResult> { 81 + return await this.collection.deleteMany(query); 82 + } 83 + 84 + async deleteOne(query: Filter<z.infer<T>>): Promise<DeleteResult> { 85 + return await this.collection.deleteOne(query); 86 + } 87 + 88 + async count(query: Filter<z.infer<T>>): Promise<number> { 89 + return await this.collection.countDocuments(query); 90 + } 91 + 92 + async aggregate(pipeline: Document[]): Promise<Document[]> { 93 + return await this.collection.aggregate(pipeline).toArray(); 94 + } 95 + 96 + // Pagination support for find 97 + async findPaginated( 98 + query: Filter<z.infer<T>>, 99 + options: { skip?: number; limit?: number; sort?: Document } = {}, 100 + ): Promise<(WithId<z.infer<T>>)[]> { 101 + return await this.collection 102 + .find(query) 103 + .skip(options.skip ?? 0) 104 + .limit(options.limit ?? 10) 105 + .sort(options.sort ?? {}) 106 + .toArray(); 107 + } 108 + }
-397
package-lock.json
··· 1 - { 2 - "name": "mizzleorm", 3 - "version": "1.0.3", 4 - "lockfileVersion": 3, 5 - "requires": true, 6 - "packages": { 7 - "": { 8 - "name": "mizzleorm", 9 - "version": "1.0.3", 10 - "license": "MIT", 11 - "dependencies": { 12 - "mongodb": "^6.17.0", 13 - "zod": "^4.0.5" 14 - }, 15 - "devDependencies": { 16 - "ts-node": "^10.9.2" 17 - } 18 - }, 19 - "node_modules/@cspotcode/source-map-support": { 20 - "version": "0.8.1", 21 - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 22 - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 23 - "dev": true, 24 - "license": "MIT", 25 - "dependencies": { 26 - "@jridgewell/trace-mapping": "0.3.9" 27 - }, 28 - "engines": { 29 - "node": ">=12" 30 - } 31 - }, 32 - "node_modules/@jridgewell/resolve-uri": { 33 - "version": "3.1.2", 34 - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 35 - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 36 - "dev": true, 37 - "license": "MIT", 38 - "engines": { 39 - "node": ">=6.0.0" 40 - } 41 - }, 42 - "node_modules/@jridgewell/sourcemap-codec": { 43 - "version": "1.5.4", 44 - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", 45 - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", 46 - "dev": true, 47 - "license": "MIT" 48 - }, 49 - "node_modules/@jridgewell/trace-mapping": { 50 - "version": "0.3.9", 51 - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 52 - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 53 - "dev": true, 54 - "license": "MIT", 55 - "dependencies": { 56 - "@jridgewell/resolve-uri": "^3.0.3", 57 - "@jridgewell/sourcemap-codec": "^1.4.10" 58 - } 59 - }, 60 - "node_modules/@mongodb-js/saslprep": { 61 - "version": "1.3.0", 62 - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz", 63 - "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==", 64 - "license": "MIT", 65 - "dependencies": { 66 - "sparse-bitfield": "^3.0.3" 67 - } 68 - }, 69 - "node_modules/@tsconfig/node10": { 70 - "version": "1.0.11", 71 - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", 72 - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", 73 - "dev": true, 74 - "license": "MIT" 75 - }, 76 - "node_modules/@tsconfig/node12": { 77 - "version": "1.0.11", 78 - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", 79 - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", 80 - "dev": true, 81 - "license": "MIT" 82 - }, 83 - "node_modules/@tsconfig/node14": { 84 - "version": "1.0.3", 85 - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", 86 - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", 87 - "dev": true, 88 - "license": "MIT" 89 - }, 90 - "node_modules/@tsconfig/node16": { 91 - "version": "1.0.4", 92 - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", 93 - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", 94 - "dev": true, 95 - "license": "MIT" 96 - }, 97 - "node_modules/@types/node": { 98 - "version": "24.1.0", 99 - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", 100 - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", 101 - "dev": true, 102 - "license": "MIT", 103 - "peer": true, 104 - "dependencies": { 105 - "undici-types": "~7.8.0" 106 - } 107 - }, 108 - "node_modules/@types/webidl-conversions": { 109 - "version": "7.0.3", 110 - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", 111 - "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", 112 - "license": "MIT" 113 - }, 114 - "node_modules/@types/whatwg-url": { 115 - "version": "11.0.5", 116 - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", 117 - "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", 118 - "license": "MIT", 119 - "dependencies": { 120 - "@types/webidl-conversions": "*" 121 - } 122 - }, 123 - "node_modules/acorn": { 124 - "version": "8.15.0", 125 - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 126 - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 127 - "dev": true, 128 - "license": "MIT", 129 - "bin": { 130 - "acorn": "bin/acorn" 131 - }, 132 - "engines": { 133 - "node": ">=0.4.0" 134 - } 135 - }, 136 - "node_modules/acorn-walk": { 137 - "version": "8.3.4", 138 - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", 139 - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", 140 - "dev": true, 141 - "license": "MIT", 142 - "dependencies": { 143 - "acorn": "^8.11.0" 144 - }, 145 - "engines": { 146 - "node": ">=0.4.0" 147 - } 148 - }, 149 - "node_modules/arg": { 150 - "version": "4.1.3", 151 - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 152 - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 153 - "dev": true, 154 - "license": "MIT" 155 - }, 156 - "node_modules/bson": { 157 - "version": "6.10.4", 158 - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", 159 - "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", 160 - "license": "Apache-2.0", 161 - "engines": { 162 - "node": ">=16.20.1" 163 - } 164 - }, 165 - "node_modules/create-require": { 166 - "version": "1.1.1", 167 - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 168 - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 169 - "dev": true, 170 - "license": "MIT" 171 - }, 172 - "node_modules/diff": { 173 - "version": "4.0.2", 174 - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 175 - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 176 - "dev": true, 177 - "license": "BSD-3-Clause", 178 - "engines": { 179 - "node": ">=0.3.1" 180 - } 181 - }, 182 - "node_modules/make-error": { 183 - "version": "1.3.6", 184 - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 185 - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 186 - "dev": true, 187 - "license": "ISC" 188 - }, 189 - "node_modules/memory-pager": { 190 - "version": "1.5.0", 191 - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 192 - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 193 - "license": "MIT" 194 - }, 195 - "node_modules/mongodb": { 196 - "version": "6.17.0", 197 - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz", 198 - "integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==", 199 - "license": "Apache-2.0", 200 - "dependencies": { 201 - "@mongodb-js/saslprep": "^1.1.9", 202 - "bson": "^6.10.4", 203 - "mongodb-connection-string-url": "^3.0.0" 204 - }, 205 - "engines": { 206 - "node": ">=16.20.1" 207 - }, 208 - "peerDependencies": { 209 - "@aws-sdk/credential-providers": "^3.188.0", 210 - "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", 211 - "gcp-metadata": "^5.2.0", 212 - "kerberos": "^2.0.1", 213 - "mongodb-client-encryption": ">=6.0.0 <7", 214 - "snappy": "^7.2.2", 215 - "socks": "^2.7.1" 216 - }, 217 - "peerDependenciesMeta": { 218 - "@aws-sdk/credential-providers": { 219 - "optional": true 220 - }, 221 - "@mongodb-js/zstd": { 222 - "optional": true 223 - }, 224 - "gcp-metadata": { 225 - "optional": true 226 - }, 227 - "kerberos": { 228 - "optional": true 229 - }, 230 - "mongodb-client-encryption": { 231 - "optional": true 232 - }, 233 - "snappy": { 234 - "optional": true 235 - }, 236 - "socks": { 237 - "optional": true 238 - } 239 - } 240 - }, 241 - "node_modules/mongodb-connection-string-url": { 242 - "version": "3.0.2", 243 - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", 244 - "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", 245 - "license": "Apache-2.0", 246 - "dependencies": { 247 - "@types/whatwg-url": "^11.0.2", 248 - "whatwg-url": "^14.1.0 || ^13.0.0" 249 - } 250 - }, 251 - "node_modules/punycode": { 252 - "version": "2.3.1", 253 - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 254 - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 255 - "license": "MIT", 256 - "engines": { 257 - "node": ">=6" 258 - } 259 - }, 260 - "node_modules/sparse-bitfield": { 261 - "version": "3.0.3", 262 - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 263 - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", 264 - "license": "MIT", 265 - "dependencies": { 266 - "memory-pager": "^1.0.2" 267 - } 268 - }, 269 - "node_modules/tr46": { 270 - "version": "5.1.1", 271 - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", 272 - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", 273 - "license": "MIT", 274 - "dependencies": { 275 - "punycode": "^2.3.1" 276 - }, 277 - "engines": { 278 - "node": ">=18" 279 - } 280 - }, 281 - "node_modules/ts-node": { 282 - "version": "10.9.2", 283 - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", 284 - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", 285 - "dev": true, 286 - "license": "MIT", 287 - "dependencies": { 288 - "@cspotcode/source-map-support": "^0.8.0", 289 - "@tsconfig/node10": "^1.0.7", 290 - "@tsconfig/node12": "^1.0.7", 291 - "@tsconfig/node14": "^1.0.0", 292 - "@tsconfig/node16": "^1.0.2", 293 - "acorn": "^8.4.1", 294 - "acorn-walk": "^8.1.1", 295 - "arg": "^4.1.0", 296 - "create-require": "^1.1.0", 297 - "diff": "^4.0.1", 298 - "make-error": "^1.1.1", 299 - "v8-compile-cache-lib": "^3.0.1", 300 - "yn": "3.1.1" 301 - }, 302 - "bin": { 303 - "ts-node": "dist/bin.js", 304 - "ts-node-cwd": "dist/bin-cwd.js", 305 - "ts-node-esm": "dist/bin-esm.js", 306 - "ts-node-script": "dist/bin-script.js", 307 - "ts-node-transpile-only": "dist/bin-transpile.js", 308 - "ts-script": "dist/bin-script-deprecated.js" 309 - }, 310 - "peerDependencies": { 311 - "@swc/core": ">=1.2.50", 312 - "@swc/wasm": ">=1.2.50", 313 - "@types/node": "*", 314 - "typescript": ">=2.7" 315 - }, 316 - "peerDependenciesMeta": { 317 - "@swc/core": { 318 - "optional": true 319 - }, 320 - "@swc/wasm": { 321 - "optional": true 322 - } 323 - } 324 - }, 325 - "node_modules/typescript": { 326 - "version": "5.8.3", 327 - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 328 - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 329 - "dev": true, 330 - "license": "Apache-2.0", 331 - "peer": true, 332 - "bin": { 333 - "tsc": "bin/tsc", 334 - "tsserver": "bin/tsserver" 335 - }, 336 - "engines": { 337 - "node": ">=14.17" 338 - } 339 - }, 340 - "node_modules/undici-types": { 341 - "version": "7.8.0", 342 - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", 343 - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", 344 - "dev": true, 345 - "license": "MIT", 346 - "peer": true 347 - }, 348 - "node_modules/v8-compile-cache-lib": { 349 - "version": "3.0.1", 350 - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", 351 - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", 352 - "dev": true, 353 - "license": "MIT" 354 - }, 355 - "node_modules/webidl-conversions": { 356 - "version": "7.0.0", 357 - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", 358 - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", 359 - "license": "BSD-2-Clause", 360 - "engines": { 361 - "node": ">=12" 362 - } 363 - }, 364 - "node_modules/whatwg-url": { 365 - "version": "14.2.0", 366 - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", 367 - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", 368 - "license": "MIT", 369 - "dependencies": { 370 - "tr46": "^5.1.0", 371 - "webidl-conversions": "^7.0.0" 372 - }, 373 - "engines": { 374 - "node": ">=18" 375 - } 376 - }, 377 - "node_modules/yn": { 378 - "version": "3.1.1", 379 - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 380 - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 381 - "dev": true, 382 - "license": "MIT", 383 - "engines": { 384 - "node": ">=6" 385 - } 386 - }, 387 - "node_modules/zod": { 388 - "version": "4.0.5", 389 - "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", 390 - "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", 391 - "license": "MIT", 392 - "funding": { 393 - "url": "https://github.com/sponsors/colinhacks" 394 - } 395 - } 396 - } 397 - }
-40
package.json
··· 1 - { 2 - "name": "mizzleorm", 3 - "version": "1.2.0", 4 - "description": "A lightweight, fully type-safe MongoDB ODM in TypeScript, inspired by Drizzle ORM.", 5 - "main": "dist/index.js", 6 - "types": "dist/index.d.ts", 7 - "scripts": { 8 - "build": "tsc", 9 - "test": "vitest", 10 - "example": "ts-node examples/user.ts" 11 - }, 12 - "keywords": [ 13 - "mongodb", 14 - "orm", 15 - "typescript", 16 - "zod", 17 - "type-safe" 18 - ], 19 - "author": "dev-shahed", 20 - "license": "MIT", 21 - "directories": { 22 - "example": "examples", 23 - "test": "tests" 24 - }, 25 - "repository": { 26 - "type": "git", 27 - "url": "git+https://github.com/dev-shahed/mizzleorm.git" 28 - }, 29 - "bugs": { 30 - "url": "https://github.com/dev-shahed/mizzleorm/issues" 31 - }, 32 - "homepage": "https://github.com/dev-shahed/mizzleorm#readme", 33 - "dependencies": { 34 - "mongodb": "^6.17.0", 35 - "zod": "^4.0.5" 36 - }, 37 - "devDependencies": { 38 - "ts-node": "^10.9.2" 39 - } 40 - }
+14
schema.ts
··· 1 + import type { z } from "zod"; 2 + import type { ObjectId } from "mongodb"; 3 + 4 + export function defineModel<T extends z.ZodObject>(schema: T) { 5 + return schema; 6 + } 7 + 8 + export type InferModel<T extends z.ZodObject> = z.infer<T> & { 9 + _id?: ObjectId; 10 + }; 11 + 12 + export type InsertType<T extends z.ZodObject> = 13 + & Omit<z.infer<T>, "createdAt"> 14 + & { createdAt?: Date };
+235
scripts/test.ts
··· 1 + #!/usr/bin/env -S deno run --allow-run --allow-read 2 + 3 + /** 4 + * Test runner script for Nozzle ORM 5 + * 6 + * Usage: 7 + * deno run --allow-run --allow-read scripts/test.ts [options] 8 + * 9 + * Options: 10 + * --mock Run only mock tests (no MongoDB required) 11 + * --real Run integration tests (requires MongoDB) 12 + * --all Run all tests (default) 13 + * --bdd Use BDD-style output for mock tests 14 + * --filter Filter tests by name pattern 15 + * --watch Watch for file changes and re-run tests 16 + * --help Show this help message 17 + */ 18 + 19 + interface TestOptions { 20 + mock?: boolean; 21 + real?: boolean; 22 + all?: boolean; 23 + bdd?: boolean; 24 + filter?: string; 25 + watch?: boolean; 26 + help?: boolean; 27 + } 28 + 29 + function parseArgs(): TestOptions { 30 + const args = Deno.args; 31 + const options: TestOptions = {}; 32 + 33 + for (let i = 0; i < args.length; i++) { 34 + const arg = args[i]; 35 + switch (arg) { 36 + case "--mock": 37 + options.mock = true; 38 + break; 39 + case "--real": 40 + options.real = true; 41 + break; 42 + case "--all": 43 + options.all = true; 44 + break; 45 + case "--bdd": 46 + options.bdd = true; 47 + break; 48 + case "--filter": 49 + options.filter = args[++i]; 50 + break; 51 + case "--watch": 52 + options.watch = true; 53 + break; 54 + case "--help": 55 + options.help = true; 56 + break; 57 + } 58 + } 59 + 60 + // Default to all tests if no specific option is provided 61 + if (!options.mock && !options.real && !options.help) { 62 + options.all = true; 63 + } 64 + 65 + return options; 66 + } 67 + 68 + function showHelp() { 69 + console.log(` 70 + 🧪 Nozzle ORM Test Runner 71 + 72 + Usage: 73 + deno run --allow-run --allow-read scripts/test.ts [options] 74 + 75 + Options: 76 + --mock Run only mock tests (no MongoDB required) 77 + --real Run integration tests (requires MongoDB) 78 + --all Run all tests (default) 79 + --bdd Use BDD-style output for mock tests 80 + --filter Filter tests by name pattern 81 + --watch Watch for file changes and re-run tests 82 + --help Show this help message 83 + 84 + Examples: 85 + scripts/test.ts # Run all tests 86 + scripts/test.ts --mock # Run only mock tests 87 + scripts/test.ts --real # Run only integration tests 88 + scripts/test.ts --mock --bdd # Run mock tests with BDD output 89 + scripts/test.ts --filter "Insert" # Run tests matching "Insert" 90 + scripts/test.ts --watch --mock # Watch and run mock tests 91 + 92 + Prerequisites for integration tests: 93 + - MongoDB running on localhost:27017 94 + - Or update connection string in tests/main_test.ts 95 + `); 96 + } 97 + 98 + async function runCommand(cmd: string[]) { 99 + const process = new Deno.Command(cmd[0], { 100 + args: cmd.slice(1), 101 + stdout: "inherit", 102 + stderr: "inherit", 103 + }); 104 + 105 + const { success, code } = await process.output(); 106 + return { success, code }; 107 + } 108 + 109 + async function runTests(options: TestOptions) { 110 + const baseCmd = ["deno", "test"]; 111 + const permissions = [ 112 + "--allow-net", 113 + "--allow-read", 114 + "--allow-write", 115 + "--allow-env", 116 + "--allow-sys", 117 + ]; 118 + 119 + if (options.help) { 120 + showHelp(); 121 + return; 122 + } 123 + 124 + console.log("🚀 Starting Nozzle Tests...\n"); 125 + 126 + if (options.mock) { 127 + console.log("📋 Running mock tests (no MongoDB required)..."); 128 + const cmd = [...baseCmd, "tests/mock_test.ts"]; 129 + if (options.bdd) { 130 + cmd.push("--reporter", "pretty"); 131 + } 132 + if (options.filter) { 133 + cmd.push("--filter", options.filter); 134 + } 135 + if (options.watch) { 136 + cmd.push("--watch"); 137 + } 138 + 139 + const result = await runCommand(cmd); 140 + if (!result.success) { 141 + console.error("❌ Mock tests failed"); 142 + Deno.exit(result.code); 143 + } else { 144 + console.log("✅ Mock tests passed!"); 145 + } 146 + } 147 + 148 + if (options.real) { 149 + console.log("🗄️ Running integration tests (MongoDB required)..."); 150 + console.log("⚠️ Make sure MongoDB is running on localhost:27017\n"); 151 + 152 + const cmd = [...baseCmd, ...permissions, "tests/main_test.ts"]; 153 + if (options.filter) { 154 + cmd.push("--filter", options.filter); 155 + } 156 + if (options.watch) { 157 + cmd.push("--watch"); 158 + } 159 + 160 + const result = await runCommand(cmd); 161 + if (!result.success) { 162 + console.error("❌ Integration tests failed"); 163 + if (result.code === 1) { 164 + console.log("\n💡 Troubleshooting tips:"); 165 + console.log( 166 + " • Ensure MongoDB is running: brew services start mongodb-community", 167 + ); 168 + console.log( 169 + " • Or start with Docker: docker run -p 27017:27017 -d mongo", 170 + ); 171 + console.log(" • Check connection at: mongodb://localhost:27017"); 172 + } 173 + Deno.exit(result.code); 174 + } else { 175 + console.log("✅ Integration tests passed!"); 176 + } 177 + } 178 + 179 + if (options.all) { 180 + console.log("🎯 Running all tests...\n"); 181 + 182 + // Run mock tests first 183 + console.log("1️⃣ Running mock tests..."); 184 + const mockCmd = [...baseCmd, "tests/mock_test.ts"]; 185 + if (options.bdd) { 186 + mockCmd.push("--reporter", "pretty"); 187 + } 188 + if (options.filter) { 189 + mockCmd.push("--filter", options.filter); 190 + } 191 + 192 + const mockResult = await runCommand(mockCmd); 193 + if (mockResult.success) { 194 + console.log("✅ Mock tests passed!\n"); 195 + } else { 196 + console.error("❌ Mock tests failed\n"); 197 + } 198 + 199 + // Run integration tests 200 + console.log("2️⃣ Running integration tests..."); 201 + console.log("⚠️ Make sure MongoDB is running on localhost:27017\n"); 202 + 203 + const integrationCmd = [...baseCmd, ...permissions, "tests/main_test.ts"]; 204 + if (options.filter) { 205 + integrationCmd.push("--filter", options.filter); 206 + } 207 + if (options.watch) { 208 + integrationCmd.push("--watch"); 209 + } 210 + 211 + const integrationResult = await runCommand(integrationCmd); 212 + 213 + if (mockResult.success && integrationResult.success) { 214 + console.log("\n🎉 All tests passed!"); 215 + } else { 216 + console.error("\n💥 Some tests failed!"); 217 + if (!integrationResult.success) { 218 + console.log("\n💡 Integration test troubleshooting:"); 219 + console.log( 220 + " • Ensure MongoDB is running: brew services start mongodb-community", 221 + ); 222 + console.log( 223 + " • Or start with Docker: docker run -p 27017:27017 -d mongo", 224 + ); 225 + console.log(" • Check connection at: mongodb://localhost:27017"); 226 + } 227 + Deno.exit(Math.max(mockResult.code, integrationResult.code)); 228 + } 229 + } 230 + } 231 + 232 + if (import.meta.main) { 233 + const options = parseArgs(); 234 + await runTests(options); 235 + }
-85
src/client.js
··· 1 - "use strict"; 2 - var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 - return new (P || (P = Promise))(function (resolve, reject) { 5 - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 - step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 - }); 10 - }; 11 - var __generator = (this && this.__generator) || function (thisArg, body) { 12 - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); 13 - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 - function verb(n) { return function (v) { return step([n, v]); }; } 15 - function step(op) { 16 - if (f) throw new TypeError("Generator is already executing."); 17 - while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 - if (y = 0, t) op = [op[0] & 2, t.value]; 20 - switch (op[0]) { 21 - case 0: case 1: t = op; break; 22 - case 4: _.label++; return { value: op[1], done: false }; 23 - case 5: _.label++; y = op[1]; op = [0]; continue; 24 - case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 - default: 26 - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 - if (t[2]) _.ops.pop(); 31 - _.trys.pop(); continue; 32 - } 33 - op = body.call(thisArg, _); 34 - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 - } 37 - }; 38 - Object.defineProperty(exports, "__esModule", { value: true }); 39 - exports.connect = connect; 40 - exports.disconnect = disconnect; 41 - exports.getDb = getDb; 42 - var mongodb_1 = require("mongodb"); 43 - var connection = null; 44 - function connect(uri, dbName) { 45 - return __awaiter(this, void 0, void 0, function () { 46 - var client, db; 47 - return __generator(this, function (_a) { 48 - switch (_a.label) { 49 - case 0: 50 - if (connection) { 51 - return [2 /*return*/, connection]; 52 - } 53 - client = new mongodb_1.MongoClient(uri); 54 - return [4 /*yield*/, client.connect()]; 55 - case 1: 56 - _a.sent(); 57 - db = client.db(dbName); 58 - connection = { client: client, db: db }; 59 - return [2 /*return*/, connection]; 60 - } 61 - }); 62 - }); 63 - } 64 - function disconnect() { 65 - return __awaiter(this, void 0, void 0, function () { 66 - return __generator(this, function (_a) { 67 - switch (_a.label) { 68 - case 0: 69 - if (!connection) return [3 /*break*/, 2]; 70 - return [4 /*yield*/, connection.client.close()]; 71 - case 1: 72 - _a.sent(); 73 - connection = null; 74 - _a.label = 2; 75 - case 2: return [2 /*return*/]; 76 - } 77 - }); 78 - }); 79 - } 80 - function getDb() { 81 - if (!connection) { 82 - throw new Error('MongoDB not connected. Call connect() first.'); 83 - } 84 - return connection.db; 85 - }
+8 -7
src/client.ts client.ts
··· 1 - import { MongoClient, Db } from 'mongodb'; 1 + import { type Db, MongoClient } from "mongodb"; 2 2 3 - interface MongoConnection { 3 + interface Connection { 4 4 client: MongoClient; 5 5 db: Db; 6 6 } 7 7 8 - let connection: MongoConnection | null = null; 8 + let connection: Connection | null = null; 9 9 10 - export async function connect(uri: string, dbName: string): Promise<MongoConnection> { 10 + export async function connect( 11 + uri: string, 12 + dbName: string, 13 + ): Promise<Connection> { 11 14 if (connection) { 12 15 return connection; 13 16 } ··· 29 32 30 33 export function getDb(): Db { 31 34 if (!connection) { 32 - throw new Error('MongoDB not connected. Call connect() first.'); 35 + throw new Error("MongoDB not connected. Call connect() first."); 33 36 } 34 37 return connection.db; 35 38 } 36 - 37 -
-10
src/index.js
··· 1 - "use strict"; 2 - Object.defineProperty(exports, "__esModule", { value: true }); 3 - exports.MongoModel = exports.disconnect = exports.connect = exports.defineModel = void 0; 4 - var schema_1 = require("./schema"); 5 - Object.defineProperty(exports, "defineModel", { enumerable: true, get: function () { return schema_1.defineModel; } }); 6 - var client_1 = require("./client"); 7 - Object.defineProperty(exports, "connect", { enumerable: true, get: function () { return client_1.connect; } }); 8 - Object.defineProperty(exports, "disconnect", { enumerable: true, get: function () { return client_1.disconnect; } }); 9 - var model_1 = require("./model"); 10 - Object.defineProperty(exports, "MongoModel", { enumerable: true, get: function () { return model_1.MongoModel; } });
-5
src/index.ts
··· 1 - export { defineModel, InferModel, InsertType } from './schema'; 2 - export { connect, disconnect } from './client'; 3 - export { MongoModel } from './model'; 4 - 5 -
-73
src/model.js
··· 1 - "use strict"; 2 - var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 - return new (P || (P = Promise))(function (resolve, reject) { 5 - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 - step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 - }); 10 - }; 11 - var __generator = (this && this.__generator) || function (thisArg, body) { 12 - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); 13 - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 - function verb(n) { return function (v) { return step([n, v]); }; } 15 - function step(op) { 16 - if (f) throw new TypeError("Generator is already executing."); 17 - while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 - if (y = 0, t) op = [op[0] & 2, t.value]; 20 - switch (op[0]) { 21 - case 0: case 1: t = op; break; 22 - case 4: _.label++; return { value: op[1], done: false }; 23 - case 5: _.label++; y = op[1]; op = [0]; continue; 24 - case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 - default: 26 - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 - if (t[2]) _.ops.pop(); 31 - _.trys.pop(); continue; 32 - } 33 - op = body.call(thisArg, _); 34 - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 - } 37 - }; 38 - Object.defineProperty(exports, "__esModule", { value: true }); 39 - exports.MongoModel = void 0; 40 - var client_1 = require("./client"); 41 - var MongoModel = /** @class */ (function () { 42 - function MongoModel(collectionName, schema) { 43 - this.collection = (0, client_1.getDb)().collection(collectionName); 44 - this.schema = schema; 45 - } 46 - MongoModel.prototype.insertOne = function (data) { 47 - return __awaiter(this, void 0, void 0, function () { 48 - var validatedData; 49 - return __generator(this, function (_a) { 50 - validatedData = this.schema.parse(data); 51 - return [2 /*return*/, this.collection.insertOne(validatedData)]; 52 - }); 53 - }); 54 - }; 55 - MongoModel.prototype.find = function (query) { 56 - return this.collection.find(query).toArray(); 57 - }; 58 - MongoModel.prototype.findOne = function (query) { 59 - return this.collection.findOne(query); 60 - }; 61 - MongoModel.prototype.update = function (query, data) { 62 - return __awaiter(this, void 0, void 0, function () { 63 - return __generator(this, function (_a) { 64 - return [2 /*return*/, this.collection.updateMany(query, { $set: data })]; 65 - }); 66 - }); 67 - }; 68 - MongoModel.prototype.delete = function (query) { 69 - return this.collection.deleteMany(query); 70 - }; 71 - return MongoModel; 72 - }()); 73 - exports.MongoModel = MongoModel;
-91
src/model.ts
··· 1 - import { z } from 'zod'; 2 - import { 3 - Collection, 4 - InsertOneResult, 5 - InsertManyResult, 6 - UpdateResult, 7 - DeleteResult, 8 - Document, 9 - ObjectId, 10 - Filter, 11 - OptionalUnlessRequiredId, 12 - WithId 13 - } from 'mongodb'; 14 - import { getDb } from './client'; 15 - import { InsertType } from './schema'; 16 - 17 - export class MongoModel<T extends z.ZodObject<any>> { 18 - private collection: Collection<z.infer<T>>; 19 - private schema: T; 20 - 21 - constructor(collectionName: string, schema: T) { 22 - this.collection = getDb().collection<z.infer<T>>(collectionName); 23 - this.schema = schema; 24 - } 25 - 26 - async insertOne(data: InsertType<T>): Promise<InsertOneResult<z.infer<T>>> { 27 - const validatedData = this.schema.parse(data); 28 - return this.collection.insertOne(validatedData as OptionalUnlessRequiredId<z.infer<T>>); 29 - } 30 - 31 - async insertMany(data: InsertType<T>[]): Promise<InsertManyResult<z.infer<T>>> { 32 - const validatedData = data.map((item) => this.schema.parse(item)); 33 - return this.collection.insertMany(validatedData as OptionalUnlessRequiredId<z.infer<T>>[]); 34 - } 35 - 36 - find(query: Filter<z.infer<T>>): Promise<(WithId<z.infer<T>>)[]> { 37 - return this.collection.find(query).toArray(); 38 - } 39 - 40 - findOne(query: Filter<z.infer<T>>): Promise<WithId<z.infer<T>> | null> { 41 - return this.collection.findOne(query); 42 - } 43 - 44 - async findById(id: string | ObjectId): Promise<WithId<z.infer<T>> | null> { 45 - const objectId = typeof id === 'string' ? new ObjectId(id) : id; 46 - return this.findOne({ _id: objectId } as Filter<z.infer<T>>); 47 - } 48 - 49 - async update(query: Filter<z.infer<T>>, data: Partial<z.infer<T>>): Promise<UpdateResult> { 50 - return this.collection.updateMany(query, { $set: data }); 51 - } 52 - 53 - async updateOne(query: Filter<z.infer<T>>, data: Partial<z.infer<T>>): Promise<UpdateResult> { 54 - return this.collection.updateOne(query, { $set: data }); 55 - } 56 - 57 - async replaceOne(query: Filter<z.infer<T>>, data: InsertType<T>): Promise<UpdateResult> { 58 - const validatedData = this.schema.parse(data); 59 - return this.collection.replaceOne(query, validatedData as OptionalUnlessRequiredId<z.infer<T>>); 60 - } 61 - 62 - async delete(query: Filter<z.infer<T>>): Promise<DeleteResult> { 63 - return this.collection.deleteMany(query); 64 - } 65 - 66 - async deleteOne(query: Filter<z.infer<T>>): Promise<DeleteResult> { 67 - return this.collection.deleteOne(query); 68 - } 69 - 70 - async count(query: Filter<z.infer<T>>): Promise<number> { 71 - return this.collection.countDocuments(query); 72 - } 73 - 74 - async aggregate(pipeline: Document[]): Promise<any[]> { 75 - return this.collection.aggregate(pipeline).toArray(); 76 - } 77 - 78 - // Pagination support for find 79 - async findPaginated( 80 - query: Filter<z.infer<T>>, 81 - options: { skip?: number; limit?: number; sort?: Document } = {} 82 - ): Promise<(WithId<z.infer<T>>)[]> { 83 - return this.collection 84 - .find(query) 85 - .skip(options.skip ?? 0) 86 - .limit(options.limit ?? 10) 87 - .sort(options.sort ?? {}) 88 - .toArray(); 89 - } 90 - } 91 -
-6
src/schema.js
··· 1 - "use strict"; 2 - Object.defineProperty(exports, "__esModule", { value: true }); 3 - exports.defineModel = defineModel; 4 - function defineModel(schema) { 5 - return schema; 6 - }
-12
src/schema.ts
··· 1 - import { z } from 'zod'; 2 - import { ObjectId } from 'mongodb'; 3 - 4 - export function defineModel<T extends z.ZodObject<any>>(schema: T) { 5 - return schema; 6 - } 7 - 8 - export type InferModel<T extends z.ZodObject<any>> = z.infer<T> & { _id?: ObjectId }; 9 - 10 - export type InsertType<T extends z.ZodObject<any>> = Omit<z.infer<T>, 'createdAt'> & { createdAt?: Date }; 11 - 12 -
+237
tests/main_test.ts
··· 1 + import { assertEquals, assertExists, assertRejects } from "jsr:@std/assert"; 2 + import { z } from "zod"; 3 + import { 4 + connect, 5 + defineModel, 6 + disconnect, 7 + type InferModel, 8 + type InsertType, 9 + Model, 10 + } from "../mod.ts"; 11 + import { ObjectId } from "mongodb"; 12 + 13 + const userSchema = defineModel(z.object({ 14 + name: z.string(), 15 + email: z.email(), 16 + age: z.number().int().positive().optional(), 17 + createdAt: z.date().default(() => new Date()), 18 + })); 19 + 20 + type User = InferModel<typeof userSchema>; 21 + type UserInsert = InsertType<typeof userSchema>; 22 + 23 + let UserModel: Model<typeof userSchema>; 24 + let isSetup = false; 25 + 26 + async function setup() { 27 + if (!isSetup) { 28 + await connect("mongodb://localhost:27017", "mizzleorm_test_db"); 29 + UserModel = new Model("users", userSchema); 30 + isSetup = true; 31 + } 32 + // Clean up before each test 33 + await UserModel.delete({}); 34 + } 35 + 36 + async function teardown() { 37 + if (isSetup) { 38 + await disconnect(); 39 + isSetup = false; 40 + } 41 + } 42 + 43 + Deno.test({ 44 + name: "Insert - should insert a new user successfully", 45 + async fn() { 46 + await setup(); 47 + 48 + const newUser: UserInsert = { 49 + name: "Test User", 50 + email: "test@example.com", 51 + age: 25, 52 + }; 53 + 54 + const insertResult = await UserModel.insertOne(newUser); 55 + 56 + assertExists(insertResult.insertedId); 57 + console.log("User inserted with ID:", insertResult.insertedId); 58 + }, 59 + sanitizeResources: false, 60 + sanitizeOps: false, 61 + }); 62 + 63 + Deno.test({ 64 + name: "Find - should find the inserted user", 65 + async fn() { 66 + await setup(); 67 + 68 + // First insert a user for this test 69 + const newUser: UserInsert = { 70 + name: "Find Test User", 71 + email: "findtest@example.com", 72 + age: 30, 73 + }; 74 + const insertResult = await UserModel.insertOne(newUser); 75 + assertExists(insertResult.insertedId); 76 + 77 + const foundUser = await UserModel.findOne({ 78 + _id: new ObjectId(insertResult.insertedId), 79 + }); 80 + 81 + assertExists(foundUser); 82 + assertEquals(foundUser.email, "findtest@example.com"); 83 + assertEquals(foundUser.name, "Find Test User"); 84 + assertEquals(foundUser.age, 30); 85 + }, 86 + sanitizeResources: false, 87 + sanitizeOps: false, 88 + }); 89 + 90 + Deno.test({ 91 + name: "Update - should update user data", 92 + async fn() { 93 + await setup(); 94 + 95 + // Insert a user for this test 96 + const newUser: UserInsert = { 97 + name: "Update Test User", 98 + email: "updatetest@example.com", 99 + age: 25, 100 + }; 101 + const insertResult = await UserModel.insertOne(newUser); 102 + assertExists(insertResult.insertedId); 103 + 104 + // Update the user 105 + const updateResult = await UserModel.update( 106 + { _id: new ObjectId(insertResult.insertedId) }, 107 + { age: 26 }, 108 + ); 109 + 110 + assertEquals(updateResult.modifiedCount, 1); 111 + 112 + // Verify the update 113 + const updatedUser = await UserModel.findOne({ 114 + _id: new ObjectId(insertResult.insertedId), 115 + }); 116 + 117 + assertExists(updatedUser); 118 + assertEquals(updatedUser.age, 26); 119 + }, 120 + sanitizeResources: false, 121 + sanitizeOps: false, 122 + }); 123 + 124 + Deno.test({ 125 + name: "Delete - should delete user successfully", 126 + async fn() { 127 + await setup(); 128 + 129 + // Insert a user for this test 130 + const newUser: UserInsert = { 131 + name: "Delete Test User", 132 + email: "deletetest@example.com", 133 + age: 35, 134 + }; 135 + const insertResult = await UserModel.insertOne(newUser); 136 + assertExists(insertResult.insertedId); 137 + 138 + // Delete the user 139 + const deleteResult = await UserModel.delete({ 140 + _id: new ObjectId(insertResult.insertedId), 141 + }); 142 + 143 + assertEquals(deleteResult.deletedCount, 1); 144 + 145 + // Verify deletion 146 + const deletedUser = await UserModel.findOne({ 147 + _id: new ObjectId(insertResult.insertedId), 148 + }); 149 + 150 + assertEquals(deletedUser, null); 151 + }, 152 + sanitizeResources: false, 153 + sanitizeOps: false, 154 + }); 155 + 156 + Deno.test({ 157 + name: "Schema Validation - should validate user data", 158 + async fn() { 159 + await setup(); 160 + 161 + const invalidUser = { 162 + name: "Invalid User", 163 + email: "not-an-email", // Invalid email 164 + age: -5, // Negative age 165 + }; 166 + 167 + // This should throw an error due to schema validation 168 + await assertRejects( 169 + async () => { 170 + await UserModel.insertOne(invalidUser as UserInsert); 171 + }, 172 + Error, 173 + ); 174 + }, 175 + sanitizeResources: false, 176 + sanitizeOps: false, 177 + }); 178 + 179 + Deno.test({ 180 + name: "Find Multiple - should find multiple users", 181 + async fn() { 182 + await setup(); 183 + 184 + // Insert multiple users 185 + const users: UserInsert[] = [ 186 + { name: "User 1", email: "user1@example.com", age: 20 }, 187 + { name: "User 2", email: "user2@example.com", age: 25 }, 188 + { name: "User 3", email: "user3@example.com", age: 30 }, 189 + ]; 190 + 191 + for (const user of users) { 192 + await UserModel.insertOne(user); 193 + } 194 + 195 + // Find all users with age >= 25 196 + const foundUsers = await UserModel.find({ age: { $gte: 25 } }); 197 + 198 + assertEquals(foundUsers.length >= 2, true); 199 + }, 200 + sanitizeResources: false, 201 + sanitizeOps: false, 202 + }); 203 + 204 + Deno.test({ 205 + name: "Default Values - should handle default createdAt", 206 + async fn() { 207 + await setup(); 208 + 209 + const newUser: UserInsert = { 210 + name: "Default Test User", 211 + email: "default@example.com", 212 + // No createdAt provided - should use default 213 + }; 214 + 215 + const insertResult = await UserModel.insertOne(newUser); 216 + assertExists(insertResult.insertedId); 217 + 218 + const foundUser = await UserModel.findOne({ 219 + _id: new ObjectId(insertResult.insertedId), 220 + }); 221 + 222 + assertExists(foundUser); 223 + assertExists(foundUser.createdAt); 224 + assertEquals(foundUser.createdAt instanceof Date, true); 225 + }, 226 + sanitizeResources: false, 227 + sanitizeOps: false, 228 + }); 229 + 230 + Deno.test({ 231 + name: "Teardown - Clean up and disconnect", 232 + async fn() { 233 + await teardown(); 234 + }, 235 + sanitizeResources: false, 236 + sanitizeOps: false, 237 + });
+452
tests/mock_test.ts
··· 1 + import { afterEach, beforeEach, describe, it } from "jsr:@std/testing/bdd"; 2 + import { assertEquals, assertExists, assertRejects } from "jsr:@std/assert"; 3 + import { z } from "zod"; 4 + 5 + // Mock implementation for demonstration 6 + class MockModel<T> { 7 + private data: Array<T & { _id: number }> = []; 8 + private idCounter = 1; 9 + 10 + constructor(private collection: string, private schema: z.ZodSchema<T>) {} 11 + 12 + insertOne(doc: z.input<z.ZodSchema<T>>) { 13 + // Validate with schema 14 + const validated = this.schema.parse(doc); 15 + const withId = { ...validated, _id: this.idCounter++ }; 16 + this.data.push(withId); 17 + return { insertedId: withId._id }; 18 + } 19 + 20 + findOne(filter: Partial<T & { _id: number }>) { 21 + if (filter._id) { 22 + return this.data.find((item) => item._id === filter._id) || null; 23 + } 24 + return this.data.find((item) => 25 + Object.entries(filter).every(([key, value]) => 26 + (item as Record<string, unknown>)[key] === value 27 + ) 28 + ) || null; 29 + } 30 + 31 + find(filter: Record<string, unknown> = {}) { 32 + return this.data.filter((item) => { 33 + return Object.entries(filter).every(([key, value]) => { 34 + if (typeof value === "object" && value !== null && "$gte" in value) { 35 + const itemValue = (item as Record<string, unknown>)[key]; 36 + const gteValue = (value as { $gte: unknown }).$gte; 37 + return typeof itemValue === "number" && 38 + typeof gteValue === "number" && itemValue >= gteValue; 39 + } 40 + return (item as Record<string, unknown>)[key] === value; 41 + }); 42 + }); 43 + } 44 + 45 + update(filter: Partial<T & { _id: number }>, update: Partial<T>) { 46 + let modifiedCount = 0; 47 + this.data = this.data.map((item) => { 48 + if (filter._id && (item as T & { _id: number })._id === filter._id) { 49 + modifiedCount++; 50 + return { ...item, ...update }; 51 + } 52 + return item; 53 + }); 54 + return { modifiedCount }; 55 + } 56 + 57 + delete(filter: Partial<T & { _id: number }>) { 58 + const initialLength = this.data.length; 59 + if (Object.keys(filter).length === 0) { 60 + // Delete all 61 + this.data = []; 62 + return { deletedCount: initialLength }; 63 + } 64 + 65 + this.data = this.data.filter((item) => { 66 + if (filter._id) { 67 + return (item as T & { _id: number })._id !== filter._id; 68 + } 69 + return !Object.entries(filter).every(([key, value]) => 70 + (item as Record<string, unknown>)[key] === value 71 + ); 72 + }); 73 + 74 + return { deletedCount: initialLength - this.data.length }; 75 + } 76 + 77 + // Helper method to clear data 78 + clear() { 79 + this.data = []; 80 + this.idCounter = 1; 81 + } 82 + } 83 + 84 + // Schema definition 85 + const userSchema = z.object({ 86 + name: z.string(), 87 + email: z.string().email(), 88 + age: z.number().int().positive().optional(), 89 + createdAt: z.date().default(() => new Date()), 90 + }); 91 + 92 + type User = z.infer<typeof userSchema>; 93 + type UserInsert = z.input<typeof userSchema>; 94 + 95 + let UserModel: MockModel<User>; 96 + 97 + describe("Mock User Model Tests", () => { 98 + beforeEach(() => { 99 + UserModel = new MockModel("users", userSchema); 100 + }); 101 + 102 + afterEach(() => { 103 + UserModel?.clear(); 104 + }); 105 + 106 + describe("Insert Operations", () => { 107 + it("should insert a new user successfully", async () => { 108 + const newUser: UserInsert = { 109 + name: "Test User", 110 + email: "test@example.com", 111 + age: 25, 112 + }; 113 + 114 + const insertResult = await UserModel.insertOne(newUser); 115 + 116 + assertExists(insertResult.insertedId); 117 + assertEquals(typeof insertResult.insertedId, "number"); 118 + }); 119 + 120 + it("should handle user without optional age", async () => { 121 + const newUser: UserInsert = { 122 + name: "User Without Age", 123 + email: "noage@example.com", 124 + }; 125 + 126 + const insertResult = await UserModel.insertOne(newUser); 127 + assertExists(insertResult.insertedId); 128 + 129 + const foundUser = await UserModel.findOne({ 130 + _id: insertResult.insertedId, 131 + }); 132 + assertEquals(foundUser?.name, "User Without Age"); 133 + assertEquals(foundUser?.age, undefined); 134 + }); 135 + 136 + it("should apply default createdAt value", async () => { 137 + const newUser: UserInsert = { 138 + name: "Default Test User", 139 + email: "default@example.com", 140 + }; 141 + 142 + const insertResult = await UserModel.insertOne(newUser); 143 + const foundUser = await UserModel.findOne({ 144 + _id: insertResult.insertedId, 145 + }); 146 + 147 + assertExists(foundUser); 148 + assertExists(foundUser.createdAt); 149 + assertEquals(foundUser.createdAt instanceof Date, true); 150 + }); 151 + }); 152 + 153 + describe("Find Operations", () => { 154 + it("should find user by ID", async () => { 155 + const newUser: UserInsert = { 156 + name: "Find Test User", 157 + email: "findtest@example.com", 158 + age: 30, 159 + }; 160 + const insertResult = await UserModel.insertOne(newUser); 161 + 162 + const foundUser = await UserModel.findOne({ 163 + _id: insertResult.insertedId, 164 + }); 165 + 166 + assertExists(foundUser); 167 + assertEquals(foundUser.email, "findtest@example.com"); 168 + assertEquals(foundUser.name, "Find Test User"); 169 + assertEquals(foundUser.age, 30); 170 + }); 171 + 172 + it("should find user by email", async () => { 173 + await UserModel.insertOne({ 174 + name: "Email Test User", 175 + email: "email@test.com", 176 + age: 28, 177 + }); 178 + 179 + const foundUser = await UserModel.findOne({ 180 + email: "email@test.com", 181 + }); 182 + 183 + assertExists(foundUser); 184 + assertEquals(foundUser.name, "Email Test User"); 185 + }); 186 + 187 + it("should return null for non-existent user", async () => { 188 + const foundUser = await UserModel.findOne({ 189 + _id: 999, 190 + }); 191 + 192 + assertEquals(foundUser, null); 193 + }); 194 + 195 + it("should find multiple users with filters", async () => { 196 + const users: UserInsert[] = [ 197 + { name: "User 1", email: "user1@example.com", age: 20 }, 198 + { name: "User 2", email: "user2@example.com", age: 25 }, 199 + { name: "User 3", email: "user3@example.com", age: 30 }, 200 + ]; 201 + 202 + for (const user of users) { 203 + await UserModel.insertOne(user); 204 + } 205 + 206 + const foundUsers = await UserModel.find({ age: { $gte: 25 } }); 207 + 208 + assertEquals(foundUsers.length, 2); 209 + assertEquals( 210 + foundUsers.every((user) => user.age !== undefined && user.age >= 25), 211 + true, 212 + ); 213 + }); 214 + 215 + it("should find all users with empty filter", async () => { 216 + await UserModel.insertOne({ 217 + name: "User A", 218 + email: "a@test.com", 219 + age: 20, 220 + }); 221 + await UserModel.insertOne({ 222 + name: "User B", 223 + email: "b@test.com", 224 + age: 25, 225 + }); 226 + 227 + const allUsers = await UserModel.find(); 228 + 229 + assertEquals(allUsers.length, 2); 230 + }); 231 + }); 232 + 233 + describe("Update Operations", () => { 234 + it("should update user data", async () => { 235 + const newUser: UserInsert = { 236 + name: "Update Test User", 237 + email: "updatetest@example.com", 238 + age: 25, 239 + }; 240 + const insertResult = await UserModel.insertOne(newUser); 241 + 242 + const updateResult = await UserModel.update( 243 + { _id: insertResult.insertedId }, 244 + { age: 26 }, 245 + ); 246 + 247 + assertEquals(updateResult.modifiedCount, 1); 248 + 249 + const updatedUser = await UserModel.findOne({ 250 + _id: insertResult.insertedId, 251 + }); 252 + assertEquals(updatedUser?.age, 26); 253 + }); 254 + 255 + it("should update multiple fields", async () => { 256 + const newUser: UserInsert = { 257 + name: "Multi Update User", 258 + email: "multi@example.com", 259 + age: 30, 260 + }; 261 + const insertResult = await UserModel.insertOne(newUser); 262 + 263 + await UserModel.update( 264 + { _id: insertResult.insertedId }, 265 + { name: "Updated Name", age: 35 }, 266 + ); 267 + 268 + const updatedUser = await UserModel.findOne({ 269 + _id: insertResult.insertedId, 270 + }); 271 + assertEquals(updatedUser?.name, "Updated Name"); 272 + assertEquals(updatedUser?.age, 35); 273 + }); 274 + 275 + it("should return 0 modified count for non-existent user", async () => { 276 + const updateResult = await UserModel.update( 277 + { _id: 999 }, 278 + { age: 100 }, 279 + ); 280 + 281 + assertEquals(updateResult.modifiedCount, 0); 282 + }); 283 + }); 284 + 285 + describe("Delete Operations", () => { 286 + it("should delete user successfully", async () => { 287 + const newUser: UserInsert = { 288 + name: "Delete Test User", 289 + email: "deletetest@example.com", 290 + age: 35, 291 + }; 292 + const insertResult = await UserModel.insertOne(newUser); 293 + 294 + const deleteResult = await UserModel.delete({ 295 + _id: insertResult.insertedId, 296 + }); 297 + 298 + assertEquals(deleteResult.deletedCount, 1); 299 + 300 + const deletedUser = await UserModel.findOne({ 301 + _id: insertResult.insertedId, 302 + }); 303 + assertEquals(deletedUser, null); 304 + }); 305 + 306 + it("should delete all users with empty filter", async () => { 307 + await UserModel.insertOne({ 308 + name: "User 1", 309 + email: "user1@test.com", 310 + }); 311 + await UserModel.insertOne({ 312 + name: "User 2", 313 + email: "user2@test.com", 314 + }); 315 + 316 + const deleteResult = await UserModel.delete({}); 317 + 318 + assertEquals(deleteResult.deletedCount, 2); 319 + 320 + const remainingUsers = await UserModel.find(); 321 + assertEquals(remainingUsers.length, 0); 322 + }); 323 + 324 + it("should return 0 deleted count for non-existent user", async () => { 325 + const deleteResult = await UserModel.delete({ 326 + _id: 999, 327 + }); 328 + 329 + assertEquals(deleteResult.deletedCount, 0); 330 + }); 331 + }); 332 + 333 + describe("Schema Validation", () => { 334 + it("should validate user data and reject invalid email", async () => { 335 + const invalidUser = { 336 + name: "Invalid User", 337 + email: "not-an-email", 338 + age: 25, 339 + }; 340 + 341 + await assertRejects( 342 + async () => { 343 + await UserModel.insertOne(invalidUser as UserInsert); 344 + }, 345 + z.ZodError, 346 + ); 347 + }); 348 + 349 + it("should reject negative age", async () => { 350 + const invalidUser = { 351 + name: "Invalid Age User", 352 + email: "valid@example.com", 353 + age: -5, 354 + }; 355 + 356 + await assertRejects( 357 + async () => { 358 + await UserModel.insertOne(invalidUser as UserInsert); 359 + }, 360 + z.ZodError, 361 + ); 362 + }); 363 + 364 + it("should reject missing required fields", async () => { 365 + const invalidUser = { 366 + age: 25, 367 + // Missing name and email 368 + }; 369 + 370 + await assertRejects( 371 + async () => { 372 + await UserModel.insertOne(invalidUser as UserInsert); 373 + }, 374 + z.ZodError, 375 + ); 376 + }); 377 + }); 378 + 379 + describe("Complex Scenarios", () => { 380 + it("should handle multiple operations in sequence", async () => { 381 + // Insert multiple users 382 + const user1 = await UserModel.insertOne({ 383 + name: "Alice", 384 + email: "alice@example.com", 385 + age: 28, 386 + }); 387 + 388 + const user2 = await UserModel.insertOne({ 389 + name: "Bob", 390 + email: "bob@example.com", 391 + age: 32, 392 + }); 393 + 394 + // Find all users 395 + const allUsers = await UserModel.find({}); 396 + assertEquals(allUsers.length, 2); 397 + 398 + // Update one user 399 + await UserModel.update({ _id: user1.insertedId }, { age: 29 }); 400 + 401 + // Delete one user 402 + await UserModel.delete({ _id: user2.insertedId }); 403 + 404 + // Verify final state 405 + const finalUsers = await UserModel.find({}); 406 + assertEquals(finalUsers.length, 1); 407 + assertEquals(finalUsers[0].name, "Alice"); 408 + assertEquals(finalUsers[0].age, 29); 409 + }); 410 + 411 + it("should maintain data isolation between operations", async () => { 412 + // This test ensures that operations don't interfere with each other 413 + const user1 = await UserModel.insertOne({ 414 + name: "Isolation Test 1", 415 + email: "iso1@test.com", 416 + age: 20, 417 + }); 418 + 419 + const user2 = await UserModel.insertOne({ 420 + name: "Isolation Test 2", 421 + email: "iso2@test.com", 422 + age: 30, 423 + }); 424 + 425 + // Update user1 shouldn't affect user2 426 + await UserModel.update({ _id: user1.insertedId }, { 427 + name: "Updated User 1", 428 + }); 429 + 430 + const foundUser2 = await UserModel.findOne({ _id: user2.insertedId }); 431 + assertEquals(foundUser2?.name, "Isolation Test 2"); // Should remain unchanged 432 + }); 433 + 434 + it("should handle concurrent-like operations", async () => { 435 + const insertPromises = [ 436 + UserModel.insertOne({ name: "Concurrent 1", email: "con1@test.com" }), 437 + UserModel.insertOne({ name: "Concurrent 2", email: "con2@test.com" }), 438 + UserModel.insertOne({ name: "Concurrent 3", email: "con3@test.com" }), 439 + ]; 440 + 441 + const results = await Promise.all(insertPromises); 442 + 443 + assertEquals(results.length, 3); 444 + results.forEach((result) => { 445 + assertExists(result.insertedId); 446 + }); 447 + 448 + const allUsers = await UserModel.find({}); 449 + assertEquals(allUsers.length, 3); 450 + }); 451 + }); 452 + });
-128
tests/user.test.js
··· 1 - "use strict"; 2 - var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 - return new (P || (P = Promise))(function (resolve, reject) { 5 - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 - step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 - }); 10 - }; 11 - var __generator = (this && this.__generator) || function (thisArg, body) { 12 - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); 13 - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 14 - function verb(n) { return function (v) { return step([n, v]); }; } 15 - function step(op) { 16 - if (f) throw new TypeError("Generator is already executing."); 17 - while (g && (g = 0, op[0] && (_ = 0)), _) try { 18 - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 19 - if (y = 0, t) op = [op[0] & 2, t.value]; 20 - switch (op[0]) { 21 - case 0: case 1: t = op; break; 22 - case 4: _.label++; return { value: op[1], done: false }; 23 - case 5: _.label++; y = op[1]; op = [0]; continue; 24 - case 7: op = _.ops.pop(); _.trys.pop(); continue; 25 - default: 26 - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 27 - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 28 - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 29 - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 30 - if (t[2]) _.ops.pop(); 31 - _.trys.pop(); continue; 32 - } 33 - op = body.call(thisArg, _); 34 - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 35 - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 36 - } 37 - }; 38 - Object.defineProperty(exports, "__esModule", { value: true }); 39 - var zod_1 = require("zod"); 40 - var src_1 = require("../src"); 41 - var mongodb_1 = require("mongodb"); 42 - var userSchema = (0, src_1.defineModel)(zod_1.z.object({ 43 - name: zod_1.z.string(), 44 - email: zod_1.z.string().email(), 45 - age: zod_1.z.number().int().positive().optional(), 46 - createdAt: zod_1.z.date().default(function () { return new Date(); }), 47 - })); 48 - function runTests() { 49 - return __awaiter(this, void 0, void 0, function () { 50 - var UserModel, newUser, insertResult, foundUser, updateResult, updatedUser, deleteResult, deletedUser, error_1; 51 - return __generator(this, function (_a) { 52 - switch (_a.label) { 53 - case 0: 54 - _a.trys.push([0, 9, 10, 12]); 55 - return [4 /*yield*/, (0, src_1.connect)('mongodb://localhost:27017', 'mizzleorm_test_db')]; 56 - case 1: 57 - _a.sent(); 58 - console.log('Connected to MongoDB for testing.'); 59 - UserModel = new src_1.MongoModel('users', userSchema); 60 - // Clean up before tests 61 - return [4 /*yield*/, UserModel.delete({})]; 62 - case 2: 63 - // Clean up before tests 64 - _a.sent(); 65 - console.log('Cleaned up existing data.'); 66 - newUser = { 67 - name: 'Test User', 68 - email: 'test@example.com', 69 - age: 25, 70 - }; 71 - return [4 /*yield*/, UserModel.insertOne(newUser)]; 72 - case 3: 73 - insertResult = _a.sent(); 74 - console.log('Test 1 (Insert): User inserted with ID:', insertResult.insertedId); 75 - if (!insertResult.insertedId) { 76 - throw new Error('Test 1 Failed: User not inserted.'); 77 - } 78 - return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 79 - case 4: 80 - foundUser = _a.sent(); 81 - console.log('Test 2 (Find One): Found user:', foundUser); 82 - if (!foundUser || foundUser.email !== 'test@example.com') { 83 - throw new Error('Test 2 Failed: User not found or data mismatch.'); 84 - } 85 - return [4 /*yield*/, UserModel.update({ _id: new mongodb_1.ObjectId(insertResult.insertedId) }, { age: 26 })]; 86 - case 5: 87 - updateResult = _a.sent(); 88 - console.log('Test 3 (Update): Modified count:', updateResult.modifiedCount); 89 - if (updateResult.modifiedCount !== 1) { 90 - throw new Error('Test 3 Failed: User not updated.'); 91 - } 92 - return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 93 - case 6: 94 - updatedUser = _a.sent(); 95 - if (!updatedUser || updatedUser.age !== 26) { 96 - throw new Error('Test 3 Failed: Updated user data mismatch.'); 97 - } 98 - return [4 /*yield*/, UserModel.delete({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 99 - case 7: 100 - deleteResult = _a.sent(); 101 - console.log('Test 4 (Delete): Deleted count:', deleteResult.deletedCount); 102 - if (deleteResult.deletedCount !== 1) { 103 - throw new Error('Test 4 Failed: User not deleted.'); 104 - } 105 - return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })]; 106 - case 8: 107 - deletedUser = _a.sent(); 108 - if (deletedUser) { 109 - throw new Error('Test 4 Failed: User still exists after deletion.'); 110 - } 111 - console.log('\nAll tests passed successfully!'); 112 - return [3 /*break*/, 12]; 113 - case 9: 114 - error_1 = _a.sent(); 115 - console.error('\nTests failed:', error_1); 116 - process.exit(1); 117 - return [3 /*break*/, 12]; 118 - case 10: return [4 /*yield*/, (0, src_1.disconnect)()]; 119 - case 11: 120 - _a.sent(); 121 - console.log('Disconnected from MongoDB.'); 122 - return [7 /*endfinally*/]; 123 - case 12: return [2 /*return*/]; 124 - } 125 - }); 126 - }); 127 - } 128 - runTests();
-83
tests/user.test.ts
··· 1 - import { z } from 'zod'; 2 - import { defineModel, MongoModel, connect, disconnect, InferModel, InsertType } from '../src'; 3 - import { ObjectId } from 'mongodb'; 4 - 5 - const userSchema = defineModel(z.object({ 6 - name: z.string(), 7 - email: z.string().email(), 8 - age: z.number().int().positive().optional(), 9 - createdAt: z.date().default(() => new Date()), 10 - })); 11 - 12 - type User = InferModel<typeof userSchema>; 13 - type UserInsert = InsertType<typeof userSchema>; 14 - 15 - async function runTests() { 16 - try { 17 - await connect('mongodb://localhost:27017', 'mizzleorm_test_db'); 18 - console.log('Connected to MongoDB for testing.'); 19 - 20 - const UserModel = new MongoModel('users', userSchema); 21 - 22 - // Clean up before tests 23 - await UserModel.delete({}); 24 - console.log('Cleaned up existing data.'); 25 - 26 - // Test 1: Insert a new user 27 - const newUser: UserInsert = { 28 - name: 'Test User', 29 - email: 'test@example.com', 30 - age: 25, 31 - }; 32 - const insertResult = await UserModel.insertOne(newUser); 33 - console.log('Test 1 (Insert): User inserted with ID:', insertResult.insertedId); 34 - if (!insertResult.insertedId) { 35 - throw new Error('Test 1 Failed: User not inserted.'); 36 - } 37 - 38 - // Test 2: Find the inserted user 39 - const foundUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); 40 - console.log('Test 2 (Find One): Found user:', foundUser); 41 - if (!foundUser || foundUser.email !== 'test@example.com') { 42 - throw new Error('Test 2 Failed: User not found or data mismatch.'); 43 - } 44 - 45 - // Test 3: Update the user 46 - const updateResult = await UserModel.update( 47 - { _id: new ObjectId(insertResult.insertedId) }, 48 - { age: 26 } 49 - ); 50 - console.log('Test 3 (Update): Modified count:', updateResult.modifiedCount); 51 - if (updateResult.modifiedCount !== 1) { 52 - throw new Error('Test 3 Failed: User not updated.'); 53 - } 54 - const updatedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); 55 - if (!updatedUser || updatedUser.age !== 26) { 56 - throw new Error('Test 3 Failed: Updated user data mismatch.'); 57 - } 58 - 59 - // Test 4: Delete the user 60 - const deleteResult = await UserModel.delete({ _id: new ObjectId(insertResult.insertedId) }); 61 - console.log('Test 4 (Delete): Deleted count:', deleteResult.deletedCount); 62 - if (deleteResult.deletedCount !== 1) { 63 - throw new Error('Test 4 Failed: User not deleted.'); 64 - } 65 - const deletedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); 66 - if (deletedUser) { 67 - throw new Error('Test 4 Failed: User still exists after deletion.'); 68 - } 69 - 70 - console.log('\nAll tests passed successfully!'); 71 - 72 - } catch (error) { 73 - console.error('\nTests failed:', error); 74 - process.exit(1); 75 - } finally { 76 - await disconnect(); 77 - console.log('Disconnected from MongoDB.'); 78 - } 79 - } 80 - 81 - runTests(); 82 - 83 -
-17
tsconfig.json
··· 1 - { 2 - "compilerOptions": { 3 - "target": "ES2020", 4 - "module": "CommonJS", 5 - "lib": ["ES2020", "DOM"], 6 - "strict": true, 7 - "esModuleInterop": true, 8 - "skipLibCheck": true, 9 - "forceConsistentCasingInFileNames": true, 10 - "outDir": "./dist", 11 - "declaration": true 12 - }, 13 - "include": ["src/**/*.ts"], 14 - "exclude": ["node_modules", "dist"] 15 - } 16 - 17 -