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
···
-
node_modules/
···
+
.DS_Store
-2
.npmignore
···
-
node_modules/
-
tests/
···
+47 -33
README.md
···
-
# **mizzleORM**
-
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.
-
-
> **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+).
## ✨ Features
-
* **Schema-first:** Define and validate collections using [Zod](https://zod.dev/).
-
* **Type-safe operations:** Auto-complete and strict typings for `insert`, `find`, `update`, and `delete`.
-
* **Minimal & modular:** No decorators or magic. Just clean, composable APIs.
-
* **Developer-friendly DX:** Great TypeScript support and IDE integration.
-
* **Built on MongoDB native driver:** Zero overhead with full control.
---
···
yarn add mizzleorm mongodb zod
```
-
> If you need to upgrade your local MongoDB server, see: https://www.mongodb.com/docs/manual/administration/install-community/
---
···
```ts
// src/schemas/user.ts
-
import { z } from 'zod';
-
import { defineModel } from 'mizzleorm';
export const userSchema = defineModel(z.object({
name: z.string(),
···
```ts
// src/index.ts
-
import { connect, disconnect, MongoModel, InferModel, InsertType } from 'mizzleorm';
-
import { userSchema } from './schemas/user';
-
import { ObjectId } from 'mongodb'; // v6+ driver recommended
type User = InferModel<typeof userSchema>;
type UserInsert = InsertType<typeof userSchema>;
async function main() {
// Use the latest connection string format and options
-
await connect('mongodb://localhost:27017', 'your_database_name');
-
const UserModel = new MongoModel('users', userSchema);
// Your operations go here
···
```ts
// Insert one
const newUser: UserInsert = {
-
name: 'John Doe',
-
email: 'john.doe@example.com',
age: 30,
};
const insertResult = await UserModel.insertOne(newUser);
// Find many
-
const users = await UserModel.find({ name: 'John Doe' });
// Find one
-
const found = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); // ObjectId from mongodb v6+
// Update
-
await UserModel.update({ name: 'John Doe' }, { age: 31 });
// Delete
-
await UserModel.delete({ name: 'John Doe' });
// Insert many
await UserModel.insertMany([
-
{ name: 'Alice', email: 'alice@example.com', age: 25 },
-
{ name: 'Bob', email: 'bob@example.com' },
]);
// Find by ID
await UserModel.findById(insertResult.insertedId);
// Update one
-
await UserModel.updateOne({ name: 'Alice' }, { age: 26 });
// Replace one
-
await UserModel.replaceOne({ name: 'Bob' }, {
-
name: 'Bob',
-
email: 'bob@newmail.com',
age: 22,
});
// Delete one
-
await UserModel.deleteOne({ name: 'Alice' });
// Count
const count = await UserModel.count({ age: { $gte: 18 } });
···
// Aggregation
const aggregation = await UserModel.aggregate([
{ $match: { age: { $gte: 18 } } },
-
{ $group: { _id: null, avgAge: { $avg: '$age' } } },
]);
// Paginated query
const paginated = await UserModel.findPaginated(
{ age: { $gte: 18 } },
-
{ skip: 0, limit: 10, sort: { age: -1 } }
);
```
···
MIT — use it freely and contribute back if you'd like!
-
---
···
+
# **Nozzle**
+
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.
+
> **Note:** Nozzle DB 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+).
## ✨ Features
+
- **Schema-first:** Define and validate collections using
+
[Zod](https://zod.dev/).
+
- **Type-safe operations:** Auto-complete and strict typings for `insert`,
+
`find`, `update`, and `delete`.
+
- **Minimal & modular:** No decorators or magic. Just clean, composable APIs.
+
- **Developer-friendly DX:** Great TypeScript support and IDE integration.
+
- **Built on MongoDB native driver:** Zero overhead with full control.
---
···
yarn add mizzleorm mongodb zod
```
+
> If you need to upgrade your local MongoDB server, see:
+
> https://www.mongodb.com/docs/manual/administration/install-community/
---
···
```ts
// src/schemas/user.ts
+
import { z } from "zod";
+
import { defineModel } from "mizzleorm";
export const userSchema = defineModel(z.object({
name: z.string(),
···
```ts
// src/index.ts
+
import {
+
connect,
+
disconnect,
+
InferModel,
+
InsertType,
+
MongoModel,
+
} from "mizzleorm";
+
import { userSchema } from "./schemas/user";
+
import { ObjectId } from "mongodb"; // v6+ driver recommended
type User = InferModel<typeof userSchema>;
type UserInsert = InsertType<typeof userSchema>;
async function main() {
// Use the latest connection string format and options
+
await connect("mongodb://localhost:27017", "your_database_name");
+
const UserModel = new MongoModel("users", userSchema);
// Your operations go here
···
```ts
// Insert one
const newUser: UserInsert = {
+
name: "John Doe",
+
email: "john.doe@example.com",
age: 30,
};
const insertResult = await UserModel.insertOne(newUser);
// Find many
+
const users = await UserModel.find({ name: "John Doe" });
// Find one
+
const found = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
}); // ObjectId from mongodb v6+
// Update
+
await UserModel.update({ name: "John Doe" }, { age: 31 });
// Delete
+
await UserModel.delete({ name: "John Doe" });
// Insert many
await UserModel.insertMany([
+
{ name: "Alice", email: "alice@example.com", age: 25 },
+
{ name: "Bob", email: "bob@example.com" },
]);
// Find by ID
await UserModel.findById(insertResult.insertedId);
// Update one
+
await UserModel.updateOne({ name: "Alice" }, { age: 26 });
// Replace one
+
await UserModel.replaceOne({ name: "Bob" }, {
+
name: "Bob",
+
email: "bob@newmail.com",
age: 22,
});
// Delete one
+
await UserModel.deleteOne({ name: "Alice" });
// Count
const count = await UserModel.count({ age: { $gte: 18 } });
···
// Aggregation
const aggregation = await UserModel.aggregate([
{ $match: { age: { $gte: 18 } } },
+
{ $group: { _id: null, avgAge: { $avg: "$age" } } },
]);
// Paginated query
const paginated = await UserModel.findPaginated(
{ age: { $gte: 18 } },
+
{ skip: 0, limit: 10, sort: { age: -1 } },
);
```
···
MIT — use it freely and contribute back if you'd like!
+
---
+18
deno.json
···
···
+
{
+
"name": "mizzleorm",
+
"version": "0.1.0",
+
"exports": "./mod.ts",
+
"license": "MIT",
+
"tasks": {
+
"build": "tsc",
+
"test": "deno run --allow-run --allow-read scripts/test.ts --all",
+
"test:mock": "deno test tests/mock_test.ts",
+
"test:integration": "deno test --allow-net --allow-read --allow-write --allow-env --allow-sys tests/main_test.ts",
+
"test:watch": "deno run --allow-run --allow-read scripts/test.ts --mock --watch",
+
"example": "ts-node examples/user.ts"
+
},
+
"imports": {
+
"zod": "jsr:@zod/zod@^4.0.17",
+
"mongodb": "npm:mongodb@^6.18.0"
+
}
+
}
+112
deno.lock
···
···
+
{
+
"version": "5",
+
"specifiers": {
+
"jsr:@std/assert@*": "1.0.13",
+
"jsr:@std/assert@^1.0.13": "1.0.13",
+
"jsr:@std/internal@^1.0.10": "1.0.10",
+
"jsr:@std/internal@^1.0.6": "1.0.10",
+
"jsr:@std/testing@*": "1.0.15",
+
"jsr:@zod/zod@^4.0.17": "4.0.17",
+
"npm:@types/node@*": "22.15.15",
+
"npm:mongodb@^6.18.0": "6.18.0"
+
},
+
"jsr": {
+
"@std/assert@1.0.13": {
+
"integrity": "ae0d31e41919b12c656c742b22522c32fb26ed0cba32975cb0de2a273cb68b29",
+
"dependencies": [
+
"jsr:@std/internal@^1.0.6"
+
]
+
},
+
"@std/internal@1.0.10": {
+
"integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7"
+
},
+
"@std/testing@1.0.15": {
+
"integrity": "a490169f5ccb0f3ae9c94fbc69d2cd43603f2cffb41713a85f99bbb0e3087cbc",
+
"dependencies": [
+
"jsr:@std/assert@^1.0.13",
+
"jsr:@std/internal@^1.0.10"
+
]
+
},
+
"@zod/zod@4.0.17": {
+
"integrity": "4d9be90a1a3c16e09dad7ce25986379d7ab8ed5f5f843288509af6bf8def525f"
+
}
+
},
+
"npm": {
+
"@mongodb-js/saslprep@1.3.0": {
+
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
+
"dependencies": [
+
"sparse-bitfield"
+
]
+
},
+
"@types/node@22.15.15": {
+
"integrity": "sha512-R5muMcZob3/Jjchn5LcO8jdKwSCbzqmPB6ruBxMcf9kbxtniZHP327s6C37iOfuw8mbKK3cAQa7sEl7afLrQ8A==",
+
"dependencies": [
+
"undici-types"
+
]
+
},
+
"@types/webidl-conversions@7.0.3": {
+
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
+
},
+
"@types/whatwg-url@11.0.5": {
+
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
+
"dependencies": [
+
"@types/webidl-conversions"
+
]
+
},
+
"bson@6.10.4": {
+
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="
+
},
+
"memory-pager@1.5.0": {
+
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
+
},
+
"mongodb-connection-string-url@3.0.2": {
+
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
+
"dependencies": [
+
"@types/whatwg-url",
+
"whatwg-url"
+
]
+
},
+
"mongodb@6.18.0": {
+
"integrity": "sha512-fO5ttN9VC8P0F5fqtQmclAkgXZxbIkYRTUi1j8JO6IYwvamkhtYDilJr35jOPELR49zqCJgXZWwCtW7B+TM8vQ==",
+
"dependencies": [
+
"@mongodb-js/saslprep",
+
"bson",
+
"mongodb-connection-string-url"
+
]
+
},
+
"punycode@2.3.1": {
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+
},
+
"sparse-bitfield@3.0.3": {
+
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+
"dependencies": [
+
"memory-pager"
+
]
+
},
+
"tr46@5.1.1": {
+
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+
"dependencies": [
+
"punycode"
+
]
+
},
+
"undici-types@6.21.0": {
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
+
},
+
"webidl-conversions@7.0.0": {
+
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+
},
+
"whatwg-url@14.2.0": {
+
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+
"dependencies": [
+
"tr46",
+
"webidl-conversions"
+
]
+
}
+
},
+
"workspace": {
+
"dependencies": [
+
"jsr:@zod/zod@^4.0.17",
+
"npm:mongodb@^6.18.0"
+
]
+
}
+
}
+220 -97
examples/user.js
···
"use strict";
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
-
};
var __generator = (this && this.__generator) || function (thisArg, body) {
-
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);
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
-
function verb(n) { return function (v) { return step([n, v]); }; }
-
function step(op) {
-
if (f) throw new TypeError("Generator is already executing.");
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
-
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;
-
if (y = 0, t) op = [op[0] & 2, t.value];
-
switch (op[0]) {
-
case 0: case 1: t = op; break;
-
case 4: _.label++; return { value: op[1], done: false };
-
case 5: _.label++; y = op[1]; op = [0]; continue;
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
-
default:
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
-
if (t[2]) _.ops.pop();
-
_.trys.pop(); continue;
}
-
op = body.call(thisArg, _);
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var zod_1 = require("zod");
···
var mongodb_1 = require("mongodb");
// 1. Define your schema using Zod
var userSchema = (0, src_1.defineModel)(zod_1.z.object({
-
name: zod_1.z.string(),
-
email: zod_1.z.string().email(),
-
age: zod_1.z.number().int().positive().optional(),
-
createdAt: zod_1.z.date().default(function () { return new Date(); }),
}));
function runExample() {
-
return __awaiter(this, void 0, void 0, function () {
-
var UserModel, newUser, insertResult, users, foundUser, updateResult, updatedUser, deleteResult, error_1;
-
return __generator(this, function (_a) {
-
switch (_a.label) {
-
case 0:
-
_a.trys.push([0, 9, 10, 12]);
-
// 3. Connect to MongoDB
-
return [4 /*yield*/, (0, src_1.connect)('mongodb://localhost:27017', 'mizzleorm_example')];
-
case 1:
-
// 3. Connect to MongoDB
-
_a.sent();
-
console.log('Connected to MongoDB');
-
UserModel = new src_1.MongoModel('users', userSchema);
-
// Clean up previous data
-
return [4 /*yield*/, UserModel.delete({})];
-
case 2:
-
// Clean up previous data
-
_a.sent();
-
newUser = {
-
name: 'Alice Smith',
-
email: 'alice@example.com',
-
age: 30,
-
};
-
return [4 /*yield*/, UserModel.insertOne(newUser)];
-
case 3:
-
insertResult = _a.sent();
-
console.log('Inserted user:', insertResult.insertedId);
-
return [4 /*yield*/, UserModel.find({ name: 'Alice Smith' })];
-
case 4:
-
users = _a.sent();
-
console.log('Found users:', users);
-
return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 5:
-
foundUser = _a.sent();
-
console.log('Found one user:', foundUser);
-
return [4 /*yield*/, UserModel.update({ _id: new mongodb_1.ObjectId(insertResult.insertedId) }, { age: 31 })];
-
case 6:
-
updateResult = _a.sent();
-
console.log('Updated user count:', updateResult.modifiedCount);
-
return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 7:
-
updatedUser = _a.sent();
-
console.log('Updated user data:', updatedUser);
-
return [4 /*yield*/, UserModel.delete({ name: 'Alice Smith' })];
-
case 8:
-
deleteResult = _a.sent();
-
console.log('Deleted user count:', deleteResult.deletedCount);
-
return [3 /*break*/, 12];
-
case 9:
-
error_1 = _a.sent();
-
console.error('Error during example run:', error_1);
-
return [3 /*break*/, 12];
-
case 10:
-
// 9. Disconnect from MongoDB
-
return [4 /*yield*/, (0, src_1.disconnect)()];
-
case 11:
-
// 9. Disconnect from MongoDB
-
_a.sent();
-
console.log('Disconnected from MongoDB');
-
return [7 /*endfinally*/];
-
case 12: return [2 /*return*/];
-
}
-
});
});
}
runExample();
···
"use strict";
+
var __awaiter = (this && this.__awaiter) ||
+
function (thisArg, _arguments, P, generator) {
+
function adopt(value) {
+
return value instanceof P ? value : new P(function (resolve) {
+
resolve(value);
+
});
+
}
return new (P || (P = Promise))(function (resolve, reject) {
+
function fulfilled(value) {
+
try {
+
step(generator.next(value));
+
} catch (e) {
+
reject(e);
+
}
+
}
+
function rejected(value) {
+
try {
+
step(generator["throw"](value));
+
} catch (e) {
+
reject(e);
+
}
+
}
+
function step(result) {
+
result.done
+
? resolve(result.value)
+
: adopt(result.value).then(fulfilled, rejected);
+
}
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
+
};
var __generator = (this && this.__generator) || function (thisArg, body) {
+
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,
+
);
+
return g.next = verb(0),
+
g["throw"] = verb(1),
+
g["return"] = verb(2),
+
typeof Symbol === "function" && (g[Symbol.iterator] = function () {
+
return this;
+
}),
+
g;
+
function verb(n) {
+
return function (v) {
+
return step([n, v]);
+
};
+
}
+
function step(op) {
+
if (f) throw new TypeError("Generator is already executing.");
+
while (g && (g = 0, op[0] && (_ = 0)), _) {
+
try {
+
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;
+
if (y = 0, t) op = [op[0] & 2, t.value];
+
switch (op[0]) {
+
case 0:
+
case 1:
+
t = op;
+
break;
+
case 4:
+
_.label++;
+
return { value: op[1], done: false };
+
case 5:
+
_.label++;
+
y = op[1];
+
op = [0];
+
continue;
+
case 7:
+
op = _.ops.pop();
+
_.trys.pop();
+
continue;
+
default:
+
if (
+
!(t = _.trys, t = t.length > 0 && t[t.length - 1]) &&
+
(op[0] === 6 || op[0] === 2)
+
) {
+
_ = 0;
+
continue;
+
}
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) {
+
_.label = op[1];
+
break;
+
}
+
if (op[0] === 6 && _.label < t[1]) {
+
_.label = t[1];
+
t = op;
+
break;
+
}
+
if (t && _.label < t[2]) {
+
_.label = t[2];
+
_.ops.push(op);
+
break;
}
+
if (t[2]) _.ops.pop();
+
_.trys.pop();
+
continue;
+
}
+
op = body.call(thisArg, _);
+
} catch (e) {
+
op = [6, e];
+
y = 0;
+
} finally {
+
f = t = 0;
+
}
}
+
if (op[0] & 5) throw op[1];
+
return { value: op[0] ? op[1] : void 0, done: true };
+
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var zod_1 = require("zod");
···
var mongodb_1 = require("mongodb");
// 1. Define your schema using Zod
var userSchema = (0, src_1.defineModel)(zod_1.z.object({
+
name: zod_1.z.string(),
+
email: zod_1.z.string().email(),
+
age: zod_1.z.number().int().positive().optional(),
+
createdAt: zod_1.z.date().default(function () {
+
return new Date();
+
}),
}));
function runExample() {
+
return __awaiter(this, void 0, void 0, function () {
+
var UserModel,
+
newUser,
+
insertResult,
+
users,
+
foundUser,
+
updateResult,
+
updatedUser,
+
deleteResult,
+
error_1;
+
return __generator(this, function (_a) {
+
switch (_a.label) {
+
case 0:
+
_a.trys.push([0, 9, 10, 12]);
+
// 3. Connect to MongoDB
+
return [
+
4, /*yield*/
+
(0, src_1.connect)(
+
"mongodb://localhost:27017",
+
"mizzleorm_example",
+
),
+
];
+
case 1:
+
// 3. Connect to MongoDB
+
_a.sent();
+
console.log("Connected to MongoDB");
+
UserModel = new src_1.MongoModel("users", userSchema);
+
// Clean up previous data
+
return [4, /*yield*/ UserModel.delete({})];
+
case 2:
+
// Clean up previous data
+
_a.sent();
+
newUser = {
+
name: "Alice Smith",
+
email: "alice@example.com",
+
age: 30,
+
};
+
return [4, /*yield*/ UserModel.insertOne(newUser)];
+
case 3:
+
insertResult = _a.sent();
+
console.log("Inserted user:", insertResult.insertedId);
+
return [4, /*yield*/ UserModel.find({ name: "Alice Smith" })];
+
case 4:
+
users = _a.sent();
+
console.log("Found users:", users);
+
return [
+
4, /*yield*/
+
UserModel.findOne({
+
_id: new mongodb_1.ObjectId(insertResult.insertedId),
+
}),
+
];
+
case 5:
+
foundUser = _a.sent();
+
console.log("Found one user:", foundUser);
+
return [
+
4, /*yield*/
+
UserModel.update({
+
_id: new mongodb_1.ObjectId(insertResult.insertedId),
+
}, { age: 31 }),
+
];
+
case 6:
+
updateResult = _a.sent();
+
console.log("Updated user count:", updateResult.modifiedCount);
+
return [
+
4, /*yield*/
+
UserModel.findOne({
+
_id: new mongodb_1.ObjectId(insertResult.insertedId),
+
}),
+
];
+
case 7:
+
updatedUser = _a.sent();
+
console.log("Updated user data:", updatedUser);
+
return [4, /*yield*/ UserModel.delete({ name: "Alice Smith" })];
+
case 8:
+
deleteResult = _a.sent();
+
console.log("Deleted user count:", deleteResult.deletedCount);
+
return [3, /*break*/ 12];
+
case 9:
+
error_1 = _a.sent();
+
console.error("Error during example run:", error_1);
+
return [3, /*break*/ 12];
+
case 10:
+
// 9. Disconnect from MongoDB
+
return [4, /*yield*/ (0, src_1.disconnect)()];
+
case 11:
+
// 9. Disconnect from MongoDB
+
_a.sent();
+
console.log("Disconnected from MongoDB");
+
return [7 /*endfinally*/];
+
case 12:
+
return [2 /*return*/];
+
}
});
+
});
}
runExample();
+32 -26
examples/user.ts
···
-
import { z } from 'zod';
-
import { defineModel, MongoModel, connect, disconnect, InferModel, InsertType } from '../src';
-
import { ObjectId } from 'mongodb';
// 1. Define your schema using Zod
const userSchema = defineModel(z.object({
···
async function runExample() {
try {
// 3. Connect to MongoDB
-
await connect('mongodb://localhost:27017', 'mizzleorm_example');
-
console.log('Connected to MongoDB');
// 2. Create a MongoModel for your collection
-
const UserModel = new MongoModel('users', userSchema);
// Clean up previous data
await UserModel.delete({});
// 4. Insert a new document
const newUser: UserInsert = {
-
name: 'Alice Smith',
-
email: 'alice@example.com',
age: 30,
};
const insertResult = await UserModel.insertOne(newUser);
-
console.log('Inserted user:', insertResult.insertedId);
// 5. Find documents
-
const users = await UserModel.find({ name: 'Alice Smith' });
-
console.log('Found users:', users);
// 6. Find one document
-
const foundUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
-
console.log('Found one user:', foundUser);
// 7. Update a document
const updateResult = await UserModel.update(
{ _id: new ObjectId(insertResult.insertedId) },
-
{ age: 31 }
);
-
console.log('Updated user count:', updateResult.modifiedCount);
-
const updatedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
-
console.log('Updated user data:', updatedUser);
// 8. Delete documents
-
const deleteResult = await UserModel.delete({ name: 'Alice Smith' });
-
console.log('Deleted user count:', deleteResult.deletedCount);
-
} catch (error) {
-
console.error('Error during example run:', error);
} finally {
// 9. Disconnect from MongoDB
await disconnect();
-
console.log('Disconnected from MongoDB');
}
}
runExample();
-
-
-
-
···
+
import { z } from "zod";
+
import {
+
connect,
+
defineModel,
+
disconnect,
+
InferModel,
+
InsertType,
+
MongoModel,
+
} from "../src";
+
import { ObjectId } from "mongodb";
// 1. Define your schema using Zod
const userSchema = defineModel(z.object({
···
async function runExample() {
try {
// 3. Connect to MongoDB
+
await connect("mongodb://localhost:27017", "mizzleorm_example");
+
console.log("Connected to MongoDB");
// 2. Create a MongoModel for your collection
+
const UserModel = new MongoModel("users", userSchema);
// Clean up previous data
await UserModel.delete({});
// 4. Insert a new document
const newUser: UserInsert = {
+
name: "Alice Smith",
+
email: "alice@example.com",
age: 30,
};
const insertResult = await UserModel.insertOne(newUser);
+
console.log("Inserted user:", insertResult.insertedId);
// 5. Find documents
+
const users = await UserModel.find({ name: "Alice Smith" });
+
console.log("Found users:", users);
// 6. Find one document
+
const foundUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
console.log("Found one user:", foundUser);
// 7. Update a document
const updateResult = await UserModel.update(
{ _id: new ObjectId(insertResult.insertedId) },
+
{ age: 31 },
);
+
console.log("Updated user count:", updateResult.modifiedCount);
+
const updatedUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
console.log("Updated user data:", updatedUser);
// 8. Delete documents
+
const deleteResult = await UserModel.delete({ name: "Alice Smith" });
+
console.log("Deleted user count:", deleteResult.deletedCount);
} catch (error) {
+
console.error("Error during example run:", error);
} finally {
// 9. Disconnect from MongoDB
await disconnect();
+
console.log("Disconnected from MongoDB");
}
}
runExample();
+3
mod.ts
···
···
+
export { defineModel, type InferModel, type InsertType } from "./schema.ts";
+
export { connect, disconnect } from "./client.ts";
+
export { Model } from "./model.ts";
+108
model.ts
···
···
+
import type { z } from "zod";
+
import type {
+
Collection,
+
DeleteResult,
+
Document,
+
Filter,
+
InsertManyResult,
+
InsertOneResult,
+
OptionalUnlessRequiredId,
+
UpdateResult,
+
WithId,
+
} from "mongodb";
+
import { ObjectId } from "mongodb";
+
import { getDb } from "./client.ts";
+
import type { InsertType } from "./schema.ts";
+
+
export class Model<T extends z.ZodObject> {
+
private collection: Collection<z.infer<T>>;
+
private schema: T;
+
+
constructor(collectionName: string, schema: T) {
+
this.collection = getDb().collection<z.infer<T>>(collectionName);
+
this.schema = schema;
+
}
+
+
async insertOne(data: InsertType<T>): Promise<InsertOneResult<z.infer<T>>> {
+
const validatedData = this.schema.parse(data);
+
return await this.collection.insertOne(
+
validatedData as OptionalUnlessRequiredId<z.infer<T>>,
+
);
+
}
+
+
async insertMany(
+
data: InsertType<T>[],
+
): Promise<InsertManyResult<z.infer<T>>> {
+
const validatedData = data.map((item) => this.schema.parse(item));
+
return await this.collection.insertMany(
+
validatedData as OptionalUnlessRequiredId<z.infer<T>>[],
+
);
+
}
+
+
async find(query: Filter<z.infer<T>>): Promise<(WithId<z.infer<T>>)[]> {
+
return await this.collection.find(query).toArray();
+
}
+
+
async findOne(query: Filter<z.infer<T>>): Promise<WithId<z.infer<T>> | null> {
+
return await this.collection.findOne(query);
+
}
+
+
async findById(id: string | ObjectId): Promise<WithId<z.infer<T>> | null> {
+
const objectId = typeof id === "string" ? new ObjectId(id) : id;
+
return await this.findOne({ _id: objectId } as Filter<z.infer<T>>);
+
}
+
+
async update(
+
query: Filter<z.infer<T>>,
+
data: Partial<z.infer<T>>,
+
): Promise<UpdateResult> {
+
return await this.collection.updateMany(query, { $set: data });
+
}
+
+
async updateOne(
+
query: Filter<z.infer<T>>,
+
data: Partial<z.infer<T>>,
+
): Promise<UpdateResult> {
+
return await this.collection.updateOne(query, { $set: data });
+
}
+
+
async replaceOne(
+
query: Filter<z.infer<T>>,
+
data: InsertType<T>,
+
): Promise<UpdateResult> {
+
const validatedData = this.schema.parse(data);
+
return await this.collection.replaceOne(
+
query,
+
validatedData as OptionalUnlessRequiredId<z.infer<T>>,
+
);
+
}
+
+
async delete(query: Filter<z.infer<T>>): Promise<DeleteResult> {
+
return await this.collection.deleteMany(query);
+
}
+
+
async deleteOne(query: Filter<z.infer<T>>): Promise<DeleteResult> {
+
return await this.collection.deleteOne(query);
+
}
+
+
async count(query: Filter<z.infer<T>>): Promise<number> {
+
return await this.collection.countDocuments(query);
+
}
+
+
async aggregate(pipeline: Document[]): Promise<Document[]> {
+
return await this.collection.aggregate(pipeline).toArray();
+
}
+
+
// Pagination support for find
+
async findPaginated(
+
query: Filter<z.infer<T>>,
+
options: { skip?: number; limit?: number; sort?: Document } = {},
+
): Promise<(WithId<z.infer<T>>)[]> {
+
return await this.collection
+
.find(query)
+
.skip(options.skip ?? 0)
+
.limit(options.limit ?? 10)
+
.sort(options.sort ?? {})
+
.toArray();
+
}
+
}
-397
package-lock.json
···
-
{
-
"name": "mizzleorm",
-
"version": "1.0.3",
-
"lockfileVersion": 3,
-
"requires": true,
-
"packages": {
-
"": {
-
"name": "mizzleorm",
-
"version": "1.0.3",
-
"license": "MIT",
-
"dependencies": {
-
"mongodb": "^6.17.0",
-
"zod": "^4.0.5"
-
},
-
"devDependencies": {
-
"ts-node": "^10.9.2"
-
}
-
},
-
"node_modules/@cspotcode/source-map-support": {
-
"version": "0.8.1",
-
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
-
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
-
"dev": true,
-
"license": "MIT",
-
"dependencies": {
-
"@jridgewell/trace-mapping": "0.3.9"
-
},
-
"engines": {
-
"node": ">=12"
-
}
-
},
-
"node_modules/@jridgewell/resolve-uri": {
-
"version": "3.1.2",
-
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
-
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
-
"dev": true,
-
"license": "MIT",
-
"engines": {
-
"node": ">=6.0.0"
-
}
-
},
-
"node_modules/@jridgewell/sourcemap-codec": {
-
"version": "1.5.4",
-
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
-
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/@jridgewell/trace-mapping": {
-
"version": "0.3.9",
-
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
-
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
-
"dev": true,
-
"license": "MIT",
-
"dependencies": {
-
"@jridgewell/resolve-uri": "^3.0.3",
-
"@jridgewell/sourcemap-codec": "^1.4.10"
-
}
-
},
-
"node_modules/@mongodb-js/saslprep": {
-
"version": "1.3.0",
-
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.0.tgz",
-
"integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
-
"license": "MIT",
-
"dependencies": {
-
"sparse-bitfield": "^3.0.3"
-
}
-
},
-
"node_modules/@tsconfig/node10": {
-
"version": "1.0.11",
-
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
-
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/@tsconfig/node12": {
-
"version": "1.0.11",
-
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
-
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/@tsconfig/node14": {
-
"version": "1.0.3",
-
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
-
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/@tsconfig/node16": {
-
"version": "1.0.4",
-
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
-
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/@types/node": {
-
"version": "24.1.0",
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
-
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
-
"dev": true,
-
"license": "MIT",
-
"peer": true,
-
"dependencies": {
-
"undici-types": "~7.8.0"
-
}
-
},
-
"node_modules/@types/webidl-conversions": {
-
"version": "7.0.3",
-
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
-
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==",
-
"license": "MIT"
-
},
-
"node_modules/@types/whatwg-url": {
-
"version": "11.0.5",
-
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz",
-
"integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==",
-
"license": "MIT",
-
"dependencies": {
-
"@types/webidl-conversions": "*"
-
}
-
},
-
"node_modules/acorn": {
-
"version": "8.15.0",
-
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
-
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
-
"dev": true,
-
"license": "MIT",
-
"bin": {
-
"acorn": "bin/acorn"
-
},
-
"engines": {
-
"node": ">=0.4.0"
-
}
-
},
-
"node_modules/acorn-walk": {
-
"version": "8.3.4",
-
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
-
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
-
"dev": true,
-
"license": "MIT",
-
"dependencies": {
-
"acorn": "^8.11.0"
-
},
-
"engines": {
-
"node": ">=0.4.0"
-
}
-
},
-
"node_modules/arg": {
-
"version": "4.1.3",
-
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
-
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/bson": {
-
"version": "6.10.4",
-
"resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz",
-
"integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==",
-
"license": "Apache-2.0",
-
"engines": {
-
"node": ">=16.20.1"
-
}
-
},
-
"node_modules/create-require": {
-
"version": "1.1.1",
-
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
-
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/diff": {
-
"version": "4.0.2",
-
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
-
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
-
"dev": true,
-
"license": "BSD-3-Clause",
-
"engines": {
-
"node": ">=0.3.1"
-
}
-
},
-
"node_modules/make-error": {
-
"version": "1.3.6",
-
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
-
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
-
"dev": true,
-
"license": "ISC"
-
},
-
"node_modules/memory-pager": {
-
"version": "1.5.0",
-
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
-
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
-
"license": "MIT"
-
},
-
"node_modules/mongodb": {
-
"version": "6.17.0",
-
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz",
-
"integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==",
-
"license": "Apache-2.0",
-
"dependencies": {
-
"@mongodb-js/saslprep": "^1.1.9",
-
"bson": "^6.10.4",
-
"mongodb-connection-string-url": "^3.0.0"
-
},
-
"engines": {
-
"node": ">=16.20.1"
-
},
-
"peerDependencies": {
-
"@aws-sdk/credential-providers": "^3.188.0",
-
"@mongodb-js/zstd": "^1.1.0 || ^2.0.0",
-
"gcp-metadata": "^5.2.0",
-
"kerberos": "^2.0.1",
-
"mongodb-client-encryption": ">=6.0.0 <7",
-
"snappy": "^7.2.2",
-
"socks": "^2.7.1"
-
},
-
"peerDependenciesMeta": {
-
"@aws-sdk/credential-providers": {
-
"optional": true
-
},
-
"@mongodb-js/zstd": {
-
"optional": true
-
},
-
"gcp-metadata": {
-
"optional": true
-
},
-
"kerberos": {
-
"optional": true
-
},
-
"mongodb-client-encryption": {
-
"optional": true
-
},
-
"snappy": {
-
"optional": true
-
},
-
"socks": {
-
"optional": true
-
}
-
}
-
},
-
"node_modules/mongodb-connection-string-url": {
-
"version": "3.0.2",
-
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
-
"integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
-
"license": "Apache-2.0",
-
"dependencies": {
-
"@types/whatwg-url": "^11.0.2",
-
"whatwg-url": "^14.1.0 || ^13.0.0"
-
}
-
},
-
"node_modules/punycode": {
-
"version": "2.3.1",
-
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
-
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
-
"license": "MIT",
-
"engines": {
-
"node": ">=6"
-
}
-
},
-
"node_modules/sparse-bitfield": {
-
"version": "3.0.3",
-
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-
"integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
-
"license": "MIT",
-
"dependencies": {
-
"memory-pager": "^1.0.2"
-
}
-
},
-
"node_modules/tr46": {
-
"version": "5.1.1",
-
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
-
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
-
"license": "MIT",
-
"dependencies": {
-
"punycode": "^2.3.1"
-
},
-
"engines": {
-
"node": ">=18"
-
}
-
},
-
"node_modules/ts-node": {
-
"version": "10.9.2",
-
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
-
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
-
"dev": true,
-
"license": "MIT",
-
"dependencies": {
-
"@cspotcode/source-map-support": "^0.8.0",
-
"@tsconfig/node10": "^1.0.7",
-
"@tsconfig/node12": "^1.0.7",
-
"@tsconfig/node14": "^1.0.0",
-
"@tsconfig/node16": "^1.0.2",
-
"acorn": "^8.4.1",
-
"acorn-walk": "^8.1.1",
-
"arg": "^4.1.0",
-
"create-require": "^1.1.0",
-
"diff": "^4.0.1",
-
"make-error": "^1.1.1",
-
"v8-compile-cache-lib": "^3.0.1",
-
"yn": "3.1.1"
-
},
-
"bin": {
-
"ts-node": "dist/bin.js",
-
"ts-node-cwd": "dist/bin-cwd.js",
-
"ts-node-esm": "dist/bin-esm.js",
-
"ts-node-script": "dist/bin-script.js",
-
"ts-node-transpile-only": "dist/bin-transpile.js",
-
"ts-script": "dist/bin-script-deprecated.js"
-
},
-
"peerDependencies": {
-
"@swc/core": ">=1.2.50",
-
"@swc/wasm": ">=1.2.50",
-
"@types/node": "*",
-
"typescript": ">=2.7"
-
},
-
"peerDependenciesMeta": {
-
"@swc/core": {
-
"optional": true
-
},
-
"@swc/wasm": {
-
"optional": true
-
}
-
}
-
},
-
"node_modules/typescript": {
-
"version": "5.8.3",
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
-
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
-
"dev": true,
-
"license": "Apache-2.0",
-
"peer": true,
-
"bin": {
-
"tsc": "bin/tsc",
-
"tsserver": "bin/tsserver"
-
},
-
"engines": {
-
"node": ">=14.17"
-
}
-
},
-
"node_modules/undici-types": {
-
"version": "7.8.0",
-
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
-
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
-
"dev": true,
-
"license": "MIT",
-
"peer": true
-
},
-
"node_modules/v8-compile-cache-lib": {
-
"version": "3.0.1",
-
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
-
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
-
"dev": true,
-
"license": "MIT"
-
},
-
"node_modules/webidl-conversions": {
-
"version": "7.0.0",
-
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
-
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
-
"license": "BSD-2-Clause",
-
"engines": {
-
"node": ">=12"
-
}
-
},
-
"node_modules/whatwg-url": {
-
"version": "14.2.0",
-
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
-
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
-
"license": "MIT",
-
"dependencies": {
-
"tr46": "^5.1.0",
-
"webidl-conversions": "^7.0.0"
-
},
-
"engines": {
-
"node": ">=18"
-
}
-
},
-
"node_modules/yn": {
-
"version": "3.1.1",
-
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
-
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
-
"dev": true,
-
"license": "MIT",
-
"engines": {
-
"node": ">=6"
-
}
-
},
-
"node_modules/zod": {
-
"version": "4.0.5",
-
"resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz",
-
"integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==",
-
"license": "MIT",
-
"funding": {
-
"url": "https://github.com/sponsors/colinhacks"
-
}
-
}
-
}
-
}
···
-40
package.json
···
-
{
-
"name": "mizzleorm",
-
"version": "1.2.0",
-
"description": "A lightweight, fully type-safe MongoDB ODM in TypeScript, inspired by Drizzle ORM.",
-
"main": "dist/index.js",
-
"types": "dist/index.d.ts",
-
"scripts": {
-
"build": "tsc",
-
"test": "vitest",
-
"example": "ts-node examples/user.ts"
-
},
-
"keywords": [
-
"mongodb",
-
"orm",
-
"typescript",
-
"zod",
-
"type-safe"
-
],
-
"author": "dev-shahed",
-
"license": "MIT",
-
"directories": {
-
"example": "examples",
-
"test": "tests"
-
},
-
"repository": {
-
"type": "git",
-
"url": "git+https://github.com/dev-shahed/mizzleorm.git"
-
},
-
"bugs": {
-
"url": "https://github.com/dev-shahed/mizzleorm/issues"
-
},
-
"homepage": "https://github.com/dev-shahed/mizzleorm#readme",
-
"dependencies": {
-
"mongodb": "^6.17.0",
-
"zod": "^4.0.5"
-
},
-
"devDependencies": {
-
"ts-node": "^10.9.2"
-
}
-
}
···
+14
schema.ts
···
···
+
import type { z } from "zod";
+
import type { ObjectId } from "mongodb";
+
+
export function defineModel<T extends z.ZodObject>(schema: T) {
+
return schema;
+
}
+
+
export type InferModel<T extends z.ZodObject> = z.infer<T> & {
+
_id?: ObjectId;
+
};
+
+
export type InsertType<T extends z.ZodObject> =
+
& Omit<z.infer<T>, "createdAt">
+
& { createdAt?: Date };
+235
scripts/test.ts
···
···
+
#!/usr/bin/env -S deno run --allow-run --allow-read
+
+
/**
+
* Test runner script for Nozzle ORM
+
*
+
* Usage:
+
* deno run --allow-run --allow-read scripts/test.ts [options]
+
*
+
* Options:
+
* --mock Run only mock tests (no MongoDB required)
+
* --real Run integration tests (requires MongoDB)
+
* --all Run all tests (default)
+
* --bdd Use BDD-style output for mock tests
+
* --filter Filter tests by name pattern
+
* --watch Watch for file changes and re-run tests
+
* --help Show this help message
+
*/
+
+
interface TestOptions {
+
mock?: boolean;
+
real?: boolean;
+
all?: boolean;
+
bdd?: boolean;
+
filter?: string;
+
watch?: boolean;
+
help?: boolean;
+
}
+
+
function parseArgs(): TestOptions {
+
const args = Deno.args;
+
const options: TestOptions = {};
+
+
for (let i = 0; i < args.length; i++) {
+
const arg = args[i];
+
switch (arg) {
+
case "--mock":
+
options.mock = true;
+
break;
+
case "--real":
+
options.real = true;
+
break;
+
case "--all":
+
options.all = true;
+
break;
+
case "--bdd":
+
options.bdd = true;
+
break;
+
case "--filter":
+
options.filter = args[++i];
+
break;
+
case "--watch":
+
options.watch = true;
+
break;
+
case "--help":
+
options.help = true;
+
break;
+
}
+
}
+
+
// Default to all tests if no specific option is provided
+
if (!options.mock && !options.real && !options.help) {
+
options.all = true;
+
}
+
+
return options;
+
}
+
+
function showHelp() {
+
console.log(`
+
🧪 Nozzle ORM Test Runner
+
+
Usage:
+
deno run --allow-run --allow-read scripts/test.ts [options]
+
+
Options:
+
--mock Run only mock tests (no MongoDB required)
+
--real Run integration tests (requires MongoDB)
+
--all Run all tests (default)
+
--bdd Use BDD-style output for mock tests
+
--filter Filter tests by name pattern
+
--watch Watch for file changes and re-run tests
+
--help Show this help message
+
+
Examples:
+
scripts/test.ts # Run all tests
+
scripts/test.ts --mock # Run only mock tests
+
scripts/test.ts --real # Run only integration tests
+
scripts/test.ts --mock --bdd # Run mock tests with BDD output
+
scripts/test.ts --filter "Insert" # Run tests matching "Insert"
+
scripts/test.ts --watch --mock # Watch and run mock tests
+
+
Prerequisites for integration tests:
+
- MongoDB running on localhost:27017
+
- Or update connection string in tests/main_test.ts
+
`);
+
}
+
+
async function runCommand(cmd: string[]) {
+
const process = new Deno.Command(cmd[0], {
+
args: cmd.slice(1),
+
stdout: "inherit",
+
stderr: "inherit",
+
});
+
+
const { success, code } = await process.output();
+
return { success, code };
+
}
+
+
async function runTests(options: TestOptions) {
+
const baseCmd = ["deno", "test"];
+
const permissions = [
+
"--allow-net",
+
"--allow-read",
+
"--allow-write",
+
"--allow-env",
+
"--allow-sys",
+
];
+
+
if (options.help) {
+
showHelp();
+
return;
+
}
+
+
console.log("🚀 Starting Nozzle Tests...\n");
+
+
if (options.mock) {
+
console.log("📋 Running mock tests (no MongoDB required)...");
+
const cmd = [...baseCmd, "tests/mock_test.ts"];
+
if (options.bdd) {
+
cmd.push("--reporter", "pretty");
+
}
+
if (options.filter) {
+
cmd.push("--filter", options.filter);
+
}
+
if (options.watch) {
+
cmd.push("--watch");
+
}
+
+
const result = await runCommand(cmd);
+
if (!result.success) {
+
console.error("❌ Mock tests failed");
+
Deno.exit(result.code);
+
} else {
+
console.log("✅ Mock tests passed!");
+
}
+
}
+
+
if (options.real) {
+
console.log("🗄️ Running integration tests (MongoDB required)...");
+
console.log("⚠️ Make sure MongoDB is running on localhost:27017\n");
+
+
const cmd = [...baseCmd, ...permissions, "tests/main_test.ts"];
+
if (options.filter) {
+
cmd.push("--filter", options.filter);
+
}
+
if (options.watch) {
+
cmd.push("--watch");
+
}
+
+
const result = await runCommand(cmd);
+
if (!result.success) {
+
console.error("❌ Integration tests failed");
+
if (result.code === 1) {
+
console.log("\n💡 Troubleshooting tips:");
+
console.log(
+
" • Ensure MongoDB is running: brew services start mongodb-community",
+
);
+
console.log(
+
" • Or start with Docker: docker run -p 27017:27017 -d mongo",
+
);
+
console.log(" • Check connection at: mongodb://localhost:27017");
+
}
+
Deno.exit(result.code);
+
} else {
+
console.log("✅ Integration tests passed!");
+
}
+
}
+
+
if (options.all) {
+
console.log("🎯 Running all tests...\n");
+
+
// Run mock tests first
+
console.log("1️⃣ Running mock tests...");
+
const mockCmd = [...baseCmd, "tests/mock_test.ts"];
+
if (options.bdd) {
+
mockCmd.push("--reporter", "pretty");
+
}
+
if (options.filter) {
+
mockCmd.push("--filter", options.filter);
+
}
+
+
const mockResult = await runCommand(mockCmd);
+
if (mockResult.success) {
+
console.log("✅ Mock tests passed!\n");
+
} else {
+
console.error("❌ Mock tests failed\n");
+
}
+
+
// Run integration tests
+
console.log("2️⃣ Running integration tests...");
+
console.log("⚠️ Make sure MongoDB is running on localhost:27017\n");
+
+
const integrationCmd = [...baseCmd, ...permissions, "tests/main_test.ts"];
+
if (options.filter) {
+
integrationCmd.push("--filter", options.filter);
+
}
+
if (options.watch) {
+
integrationCmd.push("--watch");
+
}
+
+
const integrationResult = await runCommand(integrationCmd);
+
+
if (mockResult.success && integrationResult.success) {
+
console.log("\n🎉 All tests passed!");
+
} else {
+
console.error("\n💥 Some tests failed!");
+
if (!integrationResult.success) {
+
console.log("\n💡 Integration test troubleshooting:");
+
console.log(
+
" • Ensure MongoDB is running: brew services start mongodb-community",
+
);
+
console.log(
+
" • Or start with Docker: docker run -p 27017:27017 -d mongo",
+
);
+
console.log(" • Check connection at: mongodb://localhost:27017");
+
}
+
Deno.exit(Math.max(mockResult.code, integrationResult.code));
+
}
+
}
+
}
+
+
if (import.meta.main) {
+
const options = parseArgs();
+
await runTests(options);
+
}
-85
src/client.js
···
-
"use strict";
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-
return new (P || (P = Promise))(function (resolve, reject) {
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
-
});
-
};
-
var __generator = (this && this.__generator) || function (thisArg, body) {
-
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);
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
-
function verb(n) { return function (v) { return step([n, v]); }; }
-
function step(op) {
-
if (f) throw new TypeError("Generator is already executing.");
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
-
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;
-
if (y = 0, t) op = [op[0] & 2, t.value];
-
switch (op[0]) {
-
case 0: case 1: t = op; break;
-
case 4: _.label++; return { value: op[1], done: false };
-
case 5: _.label++; y = op[1]; op = [0]; continue;
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
-
default:
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
-
if (t[2]) _.ops.pop();
-
_.trys.pop(); continue;
-
}
-
op = body.call(thisArg, _);
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
-
}
-
};
-
Object.defineProperty(exports, "__esModule", { value: true });
-
exports.connect = connect;
-
exports.disconnect = disconnect;
-
exports.getDb = getDb;
-
var mongodb_1 = require("mongodb");
-
var connection = null;
-
function connect(uri, dbName) {
-
return __awaiter(this, void 0, void 0, function () {
-
var client, db;
-
return __generator(this, function (_a) {
-
switch (_a.label) {
-
case 0:
-
if (connection) {
-
return [2 /*return*/, connection];
-
}
-
client = new mongodb_1.MongoClient(uri);
-
return [4 /*yield*/, client.connect()];
-
case 1:
-
_a.sent();
-
db = client.db(dbName);
-
connection = { client: client, db: db };
-
return [2 /*return*/, connection];
-
}
-
});
-
});
-
}
-
function disconnect() {
-
return __awaiter(this, void 0, void 0, function () {
-
return __generator(this, function (_a) {
-
switch (_a.label) {
-
case 0:
-
if (!connection) return [3 /*break*/, 2];
-
return [4 /*yield*/, connection.client.close()];
-
case 1:
-
_a.sent();
-
connection = null;
-
_a.label = 2;
-
case 2: return [2 /*return*/];
-
}
-
});
-
});
-
}
-
function getDb() {
-
if (!connection) {
-
throw new Error('MongoDB not connected. Call connect() first.');
-
}
-
return connection.db;
-
}
···
+8 -7
src/client.ts client.ts
···
-
import { MongoClient, Db } from 'mongodb';
-
interface MongoConnection {
client: MongoClient;
db: Db;
}
-
let connection: MongoConnection | null = null;
-
export async function connect(uri: string, dbName: string): Promise<MongoConnection> {
if (connection) {
return connection;
}
···
export function getDb(): Db {
if (!connection) {
-
throw new Error('MongoDB not connected. Call connect() first.');
}
return connection.db;
}
-
-
···
+
import { type Db, MongoClient } from "mongodb";
+
interface Connection {
client: MongoClient;
db: Db;
}
+
let connection: Connection | null = null;
+
export async function connect(
+
uri: string,
+
dbName: string,
+
): Promise<Connection> {
if (connection) {
return connection;
}
···
export function getDb(): Db {
if (!connection) {
+
throw new Error("MongoDB not connected. Call connect() first.");
}
return connection.db;
}
-10
src/index.js
···
-
"use strict";
-
Object.defineProperty(exports, "__esModule", { value: true });
-
exports.MongoModel = exports.disconnect = exports.connect = exports.defineModel = void 0;
-
var schema_1 = require("./schema");
-
Object.defineProperty(exports, "defineModel", { enumerable: true, get: function () { return schema_1.defineModel; } });
-
var client_1 = require("./client");
-
Object.defineProperty(exports, "connect", { enumerable: true, get: function () { return client_1.connect; } });
-
Object.defineProperty(exports, "disconnect", { enumerable: true, get: function () { return client_1.disconnect; } });
-
var model_1 = require("./model");
-
Object.defineProperty(exports, "MongoModel", { enumerable: true, get: function () { return model_1.MongoModel; } });
···
-5
src/index.ts
···
-
export { defineModel, InferModel, InsertType } from './schema';
-
export { connect, disconnect } from './client';
-
export { MongoModel } from './model';
-
-
···
-73
src/model.js
···
-
"use strict";
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-
return new (P || (P = Promise))(function (resolve, reject) {
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
-
});
-
};
-
var __generator = (this && this.__generator) || function (thisArg, body) {
-
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);
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
-
function verb(n) { return function (v) { return step([n, v]); }; }
-
function step(op) {
-
if (f) throw new TypeError("Generator is already executing.");
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
-
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;
-
if (y = 0, t) op = [op[0] & 2, t.value];
-
switch (op[0]) {
-
case 0: case 1: t = op; break;
-
case 4: _.label++; return { value: op[1], done: false };
-
case 5: _.label++; y = op[1]; op = [0]; continue;
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
-
default:
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
-
if (t[2]) _.ops.pop();
-
_.trys.pop(); continue;
-
}
-
op = body.call(thisArg, _);
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
-
}
-
};
-
Object.defineProperty(exports, "__esModule", { value: true });
-
exports.MongoModel = void 0;
-
var client_1 = require("./client");
-
var MongoModel = /** @class */ (function () {
-
function MongoModel(collectionName, schema) {
-
this.collection = (0, client_1.getDb)().collection(collectionName);
-
this.schema = schema;
-
}
-
MongoModel.prototype.insertOne = function (data) {
-
return __awaiter(this, void 0, void 0, function () {
-
var validatedData;
-
return __generator(this, function (_a) {
-
validatedData = this.schema.parse(data);
-
return [2 /*return*/, this.collection.insertOne(validatedData)];
-
});
-
});
-
};
-
MongoModel.prototype.find = function (query) {
-
return this.collection.find(query).toArray();
-
};
-
MongoModel.prototype.findOne = function (query) {
-
return this.collection.findOne(query);
-
};
-
MongoModel.prototype.update = function (query, data) {
-
return __awaiter(this, void 0, void 0, function () {
-
return __generator(this, function (_a) {
-
return [2 /*return*/, this.collection.updateMany(query, { $set: data })];
-
});
-
});
-
};
-
MongoModel.prototype.delete = function (query) {
-
return this.collection.deleteMany(query);
-
};
-
return MongoModel;
-
}());
-
exports.MongoModel = MongoModel;
···
-91
src/model.ts
···
-
import { z } from 'zod';
-
import {
-
Collection,
-
InsertOneResult,
-
InsertManyResult,
-
UpdateResult,
-
DeleteResult,
-
Document,
-
ObjectId,
-
Filter,
-
OptionalUnlessRequiredId,
-
WithId
-
} from 'mongodb';
-
import { getDb } from './client';
-
import { InsertType } from './schema';
-
-
export class MongoModel<T extends z.ZodObject<any>> {
-
private collection: Collection<z.infer<T>>;
-
private schema: T;
-
-
constructor(collectionName: string, schema: T) {
-
this.collection = getDb().collection<z.infer<T>>(collectionName);
-
this.schema = schema;
-
}
-
-
async insertOne(data: InsertType<T>): Promise<InsertOneResult<z.infer<T>>> {
-
const validatedData = this.schema.parse(data);
-
return this.collection.insertOne(validatedData as OptionalUnlessRequiredId<z.infer<T>>);
-
}
-
-
async insertMany(data: InsertType<T>[]): Promise<InsertManyResult<z.infer<T>>> {
-
const validatedData = data.map((item) => this.schema.parse(item));
-
return this.collection.insertMany(validatedData as OptionalUnlessRequiredId<z.infer<T>>[]);
-
}
-
-
find(query: Filter<z.infer<T>>): Promise<(WithId<z.infer<T>>)[]> {
-
return this.collection.find(query).toArray();
-
}
-
-
findOne(query: Filter<z.infer<T>>): Promise<WithId<z.infer<T>> | null> {
-
return this.collection.findOne(query);
-
}
-
-
async findById(id: string | ObjectId): Promise<WithId<z.infer<T>> | null> {
-
const objectId = typeof id === 'string' ? new ObjectId(id) : id;
-
return this.findOne({ _id: objectId } as Filter<z.infer<T>>);
-
}
-
-
async update(query: Filter<z.infer<T>>, data: Partial<z.infer<T>>): Promise<UpdateResult> {
-
return this.collection.updateMany(query, { $set: data });
-
}
-
-
async updateOne(query: Filter<z.infer<T>>, data: Partial<z.infer<T>>): Promise<UpdateResult> {
-
return this.collection.updateOne(query, { $set: data });
-
}
-
-
async replaceOne(query: Filter<z.infer<T>>, data: InsertType<T>): Promise<UpdateResult> {
-
const validatedData = this.schema.parse(data);
-
return this.collection.replaceOne(query, validatedData as OptionalUnlessRequiredId<z.infer<T>>);
-
}
-
-
async delete(query: Filter<z.infer<T>>): Promise<DeleteResult> {
-
return this.collection.deleteMany(query);
-
}
-
-
async deleteOne(query: Filter<z.infer<T>>): Promise<DeleteResult> {
-
return this.collection.deleteOne(query);
-
}
-
-
async count(query: Filter<z.infer<T>>): Promise<number> {
-
return this.collection.countDocuments(query);
-
}
-
-
async aggregate(pipeline: Document[]): Promise<any[]> {
-
return this.collection.aggregate(pipeline).toArray();
-
}
-
-
// Pagination support for find
-
async findPaginated(
-
query: Filter<z.infer<T>>,
-
options: { skip?: number; limit?: number; sort?: Document } = {}
-
): Promise<(WithId<z.infer<T>>)[]> {
-
return this.collection
-
.find(query)
-
.skip(options.skip ?? 0)
-
.limit(options.limit ?? 10)
-
.sort(options.sort ?? {})
-
.toArray();
-
}
-
}
-
···
-6
src/schema.js
···
-
"use strict";
-
Object.defineProperty(exports, "__esModule", { value: true });
-
exports.defineModel = defineModel;
-
function defineModel(schema) {
-
return schema;
-
}
···
-12
src/schema.ts
···
-
import { z } from 'zod';
-
import { ObjectId } from 'mongodb';
-
-
export function defineModel<T extends z.ZodObject<any>>(schema: T) {
-
return schema;
-
}
-
-
export type InferModel<T extends z.ZodObject<any>> = z.infer<T> & { _id?: ObjectId };
-
-
export type InsertType<T extends z.ZodObject<any>> = Omit<z.infer<T>, 'createdAt'> & { createdAt?: Date };
-
-
···
+237
tests/main_test.ts
···
···
+
import { assertEquals, assertExists, assertRejects } from "jsr:@std/assert";
+
import { z } from "zod";
+
import {
+
connect,
+
defineModel,
+
disconnect,
+
type InferModel,
+
type InsertType,
+
Model,
+
} from "../mod.ts";
+
import { ObjectId } from "mongodb";
+
+
const userSchema = defineModel(z.object({
+
name: z.string(),
+
email: z.email(),
+
age: z.number().int().positive().optional(),
+
createdAt: z.date().default(() => new Date()),
+
}));
+
+
type User = InferModel<typeof userSchema>;
+
type UserInsert = InsertType<typeof userSchema>;
+
+
let UserModel: Model<typeof userSchema>;
+
let isSetup = false;
+
+
async function setup() {
+
if (!isSetup) {
+
await connect("mongodb://localhost:27017", "mizzleorm_test_db");
+
UserModel = new Model("users", userSchema);
+
isSetup = true;
+
}
+
// Clean up before each test
+
await UserModel.delete({});
+
}
+
+
async function teardown() {
+
if (isSetup) {
+
await disconnect();
+
isSetup = false;
+
}
+
}
+
+
Deno.test({
+
name: "Insert - should insert a new user successfully",
+
async fn() {
+
await setup();
+
+
const newUser: UserInsert = {
+
name: "Test User",
+
email: "test@example.com",
+
age: 25,
+
};
+
+
const insertResult = await UserModel.insertOne(newUser);
+
+
assertExists(insertResult.insertedId);
+
console.log("User inserted with ID:", insertResult.insertedId);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Find - should find the inserted user",
+
async fn() {
+
await setup();
+
+
// First insert a user for this test
+
const newUser: UserInsert = {
+
name: "Find Test User",
+
email: "findtest@example.com",
+
age: 30,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
assertExists(insertResult.insertedId);
+
+
const foundUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
+
assertExists(foundUser);
+
assertEquals(foundUser.email, "findtest@example.com");
+
assertEquals(foundUser.name, "Find Test User");
+
assertEquals(foundUser.age, 30);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Update - should update user data",
+
async fn() {
+
await setup();
+
+
// Insert a user for this test
+
const newUser: UserInsert = {
+
name: "Update Test User",
+
email: "updatetest@example.com",
+
age: 25,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
assertExists(insertResult.insertedId);
+
+
// Update the user
+
const updateResult = await UserModel.update(
+
{ _id: new ObjectId(insertResult.insertedId) },
+
{ age: 26 },
+
);
+
+
assertEquals(updateResult.modifiedCount, 1);
+
+
// Verify the update
+
const updatedUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
+
assertExists(updatedUser);
+
assertEquals(updatedUser.age, 26);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Delete - should delete user successfully",
+
async fn() {
+
await setup();
+
+
// Insert a user for this test
+
const newUser: UserInsert = {
+
name: "Delete Test User",
+
email: "deletetest@example.com",
+
age: 35,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
assertExists(insertResult.insertedId);
+
+
// Delete the user
+
const deleteResult = await UserModel.delete({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
+
assertEquals(deleteResult.deletedCount, 1);
+
+
// Verify deletion
+
const deletedUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
+
assertEquals(deletedUser, null);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Schema Validation - should validate user data",
+
async fn() {
+
await setup();
+
+
const invalidUser = {
+
name: "Invalid User",
+
email: "not-an-email", // Invalid email
+
age: -5, // Negative age
+
};
+
+
// This should throw an error due to schema validation
+
await assertRejects(
+
async () => {
+
await UserModel.insertOne(invalidUser as UserInsert);
+
},
+
Error,
+
);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Find Multiple - should find multiple users",
+
async fn() {
+
await setup();
+
+
// Insert multiple users
+
const users: UserInsert[] = [
+
{ name: "User 1", email: "user1@example.com", age: 20 },
+
{ name: "User 2", email: "user2@example.com", age: 25 },
+
{ name: "User 3", email: "user3@example.com", age: 30 },
+
];
+
+
for (const user of users) {
+
await UserModel.insertOne(user);
+
}
+
+
// Find all users with age >= 25
+
const foundUsers = await UserModel.find({ age: { $gte: 25 } });
+
+
assertEquals(foundUsers.length >= 2, true);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Default Values - should handle default createdAt",
+
async fn() {
+
await setup();
+
+
const newUser: UserInsert = {
+
name: "Default Test User",
+
email: "default@example.com",
+
// No createdAt provided - should use default
+
};
+
+
const insertResult = await UserModel.insertOne(newUser);
+
assertExists(insertResult.insertedId);
+
+
const foundUser = await UserModel.findOne({
+
_id: new ObjectId(insertResult.insertedId),
+
});
+
+
assertExists(foundUser);
+
assertExists(foundUser.createdAt);
+
assertEquals(foundUser.createdAt instanceof Date, true);
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+
+
Deno.test({
+
name: "Teardown - Clean up and disconnect",
+
async fn() {
+
await teardown();
+
},
+
sanitizeResources: false,
+
sanitizeOps: false,
+
});
+452
tests/mock_test.ts
···
···
+
import { afterEach, beforeEach, describe, it } from "jsr:@std/testing/bdd";
+
import { assertEquals, assertExists, assertRejects } from "jsr:@std/assert";
+
import { z } from "zod";
+
+
// Mock implementation for demonstration
+
class MockModel<T> {
+
private data: Array<T & { _id: number }> = [];
+
private idCounter = 1;
+
+
constructor(private collection: string, private schema: z.ZodSchema<T>) {}
+
+
insertOne(doc: z.input<z.ZodSchema<T>>) {
+
// Validate with schema
+
const validated = this.schema.parse(doc);
+
const withId = { ...validated, _id: this.idCounter++ };
+
this.data.push(withId);
+
return { insertedId: withId._id };
+
}
+
+
findOne(filter: Partial<T & { _id: number }>) {
+
if (filter._id) {
+
return this.data.find((item) => item._id === filter._id) || null;
+
}
+
return this.data.find((item) =>
+
Object.entries(filter).every(([key, value]) =>
+
(item as Record<string, unknown>)[key] === value
+
)
+
) || null;
+
}
+
+
find(filter: Record<string, unknown> = {}) {
+
return this.data.filter((item) => {
+
return Object.entries(filter).every(([key, value]) => {
+
if (typeof value === "object" && value !== null && "$gte" in value) {
+
const itemValue = (item as Record<string, unknown>)[key];
+
const gteValue = (value as { $gte: unknown }).$gte;
+
return typeof itemValue === "number" &&
+
typeof gteValue === "number" && itemValue >= gteValue;
+
}
+
return (item as Record<string, unknown>)[key] === value;
+
});
+
});
+
}
+
+
update(filter: Partial<T & { _id: number }>, update: Partial<T>) {
+
let modifiedCount = 0;
+
this.data = this.data.map((item) => {
+
if (filter._id && (item as T & { _id: number })._id === filter._id) {
+
modifiedCount++;
+
return { ...item, ...update };
+
}
+
return item;
+
});
+
return { modifiedCount };
+
}
+
+
delete(filter: Partial<T & { _id: number }>) {
+
const initialLength = this.data.length;
+
if (Object.keys(filter).length === 0) {
+
// Delete all
+
this.data = [];
+
return { deletedCount: initialLength };
+
}
+
+
this.data = this.data.filter((item) => {
+
if (filter._id) {
+
return (item as T & { _id: number })._id !== filter._id;
+
}
+
return !Object.entries(filter).every(([key, value]) =>
+
(item as Record<string, unknown>)[key] === value
+
);
+
});
+
+
return { deletedCount: initialLength - this.data.length };
+
}
+
+
// Helper method to clear data
+
clear() {
+
this.data = [];
+
this.idCounter = 1;
+
}
+
}
+
+
// Schema definition
+
const userSchema = z.object({
+
name: z.string(),
+
email: z.string().email(),
+
age: z.number().int().positive().optional(),
+
createdAt: z.date().default(() => new Date()),
+
});
+
+
type User = z.infer<typeof userSchema>;
+
type UserInsert = z.input<typeof userSchema>;
+
+
let UserModel: MockModel<User>;
+
+
describe("Mock User Model Tests", () => {
+
beforeEach(() => {
+
UserModel = new MockModel("users", userSchema);
+
});
+
+
afterEach(() => {
+
UserModel?.clear();
+
});
+
+
describe("Insert Operations", () => {
+
it("should insert a new user successfully", async () => {
+
const newUser: UserInsert = {
+
name: "Test User",
+
email: "test@example.com",
+
age: 25,
+
};
+
+
const insertResult = await UserModel.insertOne(newUser);
+
+
assertExists(insertResult.insertedId);
+
assertEquals(typeof insertResult.insertedId, "number");
+
});
+
+
it("should handle user without optional age", async () => {
+
const newUser: UserInsert = {
+
name: "User Without Age",
+
email: "noage@example.com",
+
};
+
+
const insertResult = await UserModel.insertOne(newUser);
+
assertExists(insertResult.insertedId);
+
+
const foundUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
assertEquals(foundUser?.name, "User Without Age");
+
assertEquals(foundUser?.age, undefined);
+
});
+
+
it("should apply default createdAt value", async () => {
+
const newUser: UserInsert = {
+
name: "Default Test User",
+
email: "default@example.com",
+
};
+
+
const insertResult = await UserModel.insertOne(newUser);
+
const foundUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
+
assertExists(foundUser);
+
assertExists(foundUser.createdAt);
+
assertEquals(foundUser.createdAt instanceof Date, true);
+
});
+
});
+
+
describe("Find Operations", () => {
+
it("should find user by ID", async () => {
+
const newUser: UserInsert = {
+
name: "Find Test User",
+
email: "findtest@example.com",
+
age: 30,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
+
const foundUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
+
assertExists(foundUser);
+
assertEquals(foundUser.email, "findtest@example.com");
+
assertEquals(foundUser.name, "Find Test User");
+
assertEquals(foundUser.age, 30);
+
});
+
+
it("should find user by email", async () => {
+
await UserModel.insertOne({
+
name: "Email Test User",
+
email: "email@test.com",
+
age: 28,
+
});
+
+
const foundUser = await UserModel.findOne({
+
email: "email@test.com",
+
});
+
+
assertExists(foundUser);
+
assertEquals(foundUser.name, "Email Test User");
+
});
+
+
it("should return null for non-existent user", async () => {
+
const foundUser = await UserModel.findOne({
+
_id: 999,
+
});
+
+
assertEquals(foundUser, null);
+
});
+
+
it("should find multiple users with filters", async () => {
+
const users: UserInsert[] = [
+
{ name: "User 1", email: "user1@example.com", age: 20 },
+
{ name: "User 2", email: "user2@example.com", age: 25 },
+
{ name: "User 3", email: "user3@example.com", age: 30 },
+
];
+
+
for (const user of users) {
+
await UserModel.insertOne(user);
+
}
+
+
const foundUsers = await UserModel.find({ age: { $gte: 25 } });
+
+
assertEquals(foundUsers.length, 2);
+
assertEquals(
+
foundUsers.every((user) => user.age !== undefined && user.age >= 25),
+
true,
+
);
+
});
+
+
it("should find all users with empty filter", async () => {
+
await UserModel.insertOne({
+
name: "User A",
+
email: "a@test.com",
+
age: 20,
+
});
+
await UserModel.insertOne({
+
name: "User B",
+
email: "b@test.com",
+
age: 25,
+
});
+
+
const allUsers = await UserModel.find();
+
+
assertEquals(allUsers.length, 2);
+
});
+
});
+
+
describe("Update Operations", () => {
+
it("should update user data", async () => {
+
const newUser: UserInsert = {
+
name: "Update Test User",
+
email: "updatetest@example.com",
+
age: 25,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
+
const updateResult = await UserModel.update(
+
{ _id: insertResult.insertedId },
+
{ age: 26 },
+
);
+
+
assertEquals(updateResult.modifiedCount, 1);
+
+
const updatedUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
assertEquals(updatedUser?.age, 26);
+
});
+
+
it("should update multiple fields", async () => {
+
const newUser: UserInsert = {
+
name: "Multi Update User",
+
email: "multi@example.com",
+
age: 30,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
+
await UserModel.update(
+
{ _id: insertResult.insertedId },
+
{ name: "Updated Name", age: 35 },
+
);
+
+
const updatedUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
assertEquals(updatedUser?.name, "Updated Name");
+
assertEquals(updatedUser?.age, 35);
+
});
+
+
it("should return 0 modified count for non-existent user", async () => {
+
const updateResult = await UserModel.update(
+
{ _id: 999 },
+
{ age: 100 },
+
);
+
+
assertEquals(updateResult.modifiedCount, 0);
+
});
+
});
+
+
describe("Delete Operations", () => {
+
it("should delete user successfully", async () => {
+
const newUser: UserInsert = {
+
name: "Delete Test User",
+
email: "deletetest@example.com",
+
age: 35,
+
};
+
const insertResult = await UserModel.insertOne(newUser);
+
+
const deleteResult = await UserModel.delete({
+
_id: insertResult.insertedId,
+
});
+
+
assertEquals(deleteResult.deletedCount, 1);
+
+
const deletedUser = await UserModel.findOne({
+
_id: insertResult.insertedId,
+
});
+
assertEquals(deletedUser, null);
+
});
+
+
it("should delete all users with empty filter", async () => {
+
await UserModel.insertOne({
+
name: "User 1",
+
email: "user1@test.com",
+
});
+
await UserModel.insertOne({
+
name: "User 2",
+
email: "user2@test.com",
+
});
+
+
const deleteResult = await UserModel.delete({});
+
+
assertEquals(deleteResult.deletedCount, 2);
+
+
const remainingUsers = await UserModel.find();
+
assertEquals(remainingUsers.length, 0);
+
});
+
+
it("should return 0 deleted count for non-existent user", async () => {
+
const deleteResult = await UserModel.delete({
+
_id: 999,
+
});
+
+
assertEquals(deleteResult.deletedCount, 0);
+
});
+
});
+
+
describe("Schema Validation", () => {
+
it("should validate user data and reject invalid email", async () => {
+
const invalidUser = {
+
name: "Invalid User",
+
email: "not-an-email",
+
age: 25,
+
};
+
+
await assertRejects(
+
async () => {
+
await UserModel.insertOne(invalidUser as UserInsert);
+
},
+
z.ZodError,
+
);
+
});
+
+
it("should reject negative age", async () => {
+
const invalidUser = {
+
name: "Invalid Age User",
+
email: "valid@example.com",
+
age: -5,
+
};
+
+
await assertRejects(
+
async () => {
+
await UserModel.insertOne(invalidUser as UserInsert);
+
},
+
z.ZodError,
+
);
+
});
+
+
it("should reject missing required fields", async () => {
+
const invalidUser = {
+
age: 25,
+
// Missing name and email
+
};
+
+
await assertRejects(
+
async () => {
+
await UserModel.insertOne(invalidUser as UserInsert);
+
},
+
z.ZodError,
+
);
+
});
+
});
+
+
describe("Complex Scenarios", () => {
+
it("should handle multiple operations in sequence", async () => {
+
// Insert multiple users
+
const user1 = await UserModel.insertOne({
+
name: "Alice",
+
email: "alice@example.com",
+
age: 28,
+
});
+
+
const user2 = await UserModel.insertOne({
+
name: "Bob",
+
email: "bob@example.com",
+
age: 32,
+
});
+
+
// Find all users
+
const allUsers = await UserModel.find({});
+
assertEquals(allUsers.length, 2);
+
+
// Update one user
+
await UserModel.update({ _id: user1.insertedId }, { age: 29 });
+
+
// Delete one user
+
await UserModel.delete({ _id: user2.insertedId });
+
+
// Verify final state
+
const finalUsers = await UserModel.find({});
+
assertEquals(finalUsers.length, 1);
+
assertEquals(finalUsers[0].name, "Alice");
+
assertEquals(finalUsers[0].age, 29);
+
});
+
+
it("should maintain data isolation between operations", async () => {
+
// This test ensures that operations don't interfere with each other
+
const user1 = await UserModel.insertOne({
+
name: "Isolation Test 1",
+
email: "iso1@test.com",
+
age: 20,
+
});
+
+
const user2 = await UserModel.insertOne({
+
name: "Isolation Test 2",
+
email: "iso2@test.com",
+
age: 30,
+
});
+
+
// Update user1 shouldn't affect user2
+
await UserModel.update({ _id: user1.insertedId }, {
+
name: "Updated User 1",
+
});
+
+
const foundUser2 = await UserModel.findOne({ _id: user2.insertedId });
+
assertEquals(foundUser2?.name, "Isolation Test 2"); // Should remain unchanged
+
});
+
+
it("should handle concurrent-like operations", async () => {
+
const insertPromises = [
+
UserModel.insertOne({ name: "Concurrent 1", email: "con1@test.com" }),
+
UserModel.insertOne({ name: "Concurrent 2", email: "con2@test.com" }),
+
UserModel.insertOne({ name: "Concurrent 3", email: "con3@test.com" }),
+
];
+
+
const results = await Promise.all(insertPromises);
+
+
assertEquals(results.length, 3);
+
results.forEach((result) => {
+
assertExists(result.insertedId);
+
});
+
+
const allUsers = await UserModel.find({});
+
assertEquals(allUsers.length, 3);
+
});
+
});
+
});
-128
tests/user.test.js
···
-
"use strict";
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-
return new (P || (P = Promise))(function (resolve, reject) {
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
-
});
-
};
-
var __generator = (this && this.__generator) || function (thisArg, body) {
-
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);
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
-
function verb(n) { return function (v) { return step([n, v]); }; }
-
function step(op) {
-
if (f) throw new TypeError("Generator is already executing.");
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
-
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;
-
if (y = 0, t) op = [op[0] & 2, t.value];
-
switch (op[0]) {
-
case 0: case 1: t = op; break;
-
case 4: _.label++; return { value: op[1], done: false };
-
case 5: _.label++; y = op[1]; op = [0]; continue;
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
-
default:
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
-
if (t[2]) _.ops.pop();
-
_.trys.pop(); continue;
-
}
-
op = body.call(thisArg, _);
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
-
}
-
};
-
Object.defineProperty(exports, "__esModule", { value: true });
-
var zod_1 = require("zod");
-
var src_1 = require("../src");
-
var mongodb_1 = require("mongodb");
-
var userSchema = (0, src_1.defineModel)(zod_1.z.object({
-
name: zod_1.z.string(),
-
email: zod_1.z.string().email(),
-
age: zod_1.z.number().int().positive().optional(),
-
createdAt: zod_1.z.date().default(function () { return new Date(); }),
-
}));
-
function runTests() {
-
return __awaiter(this, void 0, void 0, function () {
-
var UserModel, newUser, insertResult, foundUser, updateResult, updatedUser, deleteResult, deletedUser, error_1;
-
return __generator(this, function (_a) {
-
switch (_a.label) {
-
case 0:
-
_a.trys.push([0, 9, 10, 12]);
-
return [4 /*yield*/, (0, src_1.connect)('mongodb://localhost:27017', 'mizzleorm_test_db')];
-
case 1:
-
_a.sent();
-
console.log('Connected to MongoDB for testing.');
-
UserModel = new src_1.MongoModel('users', userSchema);
-
// Clean up before tests
-
return [4 /*yield*/, UserModel.delete({})];
-
case 2:
-
// Clean up before tests
-
_a.sent();
-
console.log('Cleaned up existing data.');
-
newUser = {
-
name: 'Test User',
-
email: 'test@example.com',
-
age: 25,
-
};
-
return [4 /*yield*/, UserModel.insertOne(newUser)];
-
case 3:
-
insertResult = _a.sent();
-
console.log('Test 1 (Insert): User inserted with ID:', insertResult.insertedId);
-
if (!insertResult.insertedId) {
-
throw new Error('Test 1 Failed: User not inserted.');
-
}
-
return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 4:
-
foundUser = _a.sent();
-
console.log('Test 2 (Find One): Found user:', foundUser);
-
if (!foundUser || foundUser.email !== 'test@example.com') {
-
throw new Error('Test 2 Failed: User not found or data mismatch.');
-
}
-
return [4 /*yield*/, UserModel.update({ _id: new mongodb_1.ObjectId(insertResult.insertedId) }, { age: 26 })];
-
case 5:
-
updateResult = _a.sent();
-
console.log('Test 3 (Update): Modified count:', updateResult.modifiedCount);
-
if (updateResult.modifiedCount !== 1) {
-
throw new Error('Test 3 Failed: User not updated.');
-
}
-
return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 6:
-
updatedUser = _a.sent();
-
if (!updatedUser || updatedUser.age !== 26) {
-
throw new Error('Test 3 Failed: Updated user data mismatch.');
-
}
-
return [4 /*yield*/, UserModel.delete({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 7:
-
deleteResult = _a.sent();
-
console.log('Test 4 (Delete): Deleted count:', deleteResult.deletedCount);
-
if (deleteResult.deletedCount !== 1) {
-
throw new Error('Test 4 Failed: User not deleted.');
-
}
-
return [4 /*yield*/, UserModel.findOne({ _id: new mongodb_1.ObjectId(insertResult.insertedId) })];
-
case 8:
-
deletedUser = _a.sent();
-
if (deletedUser) {
-
throw new Error('Test 4 Failed: User still exists after deletion.');
-
}
-
console.log('\nAll tests passed successfully!');
-
return [3 /*break*/, 12];
-
case 9:
-
error_1 = _a.sent();
-
console.error('\nTests failed:', error_1);
-
process.exit(1);
-
return [3 /*break*/, 12];
-
case 10: return [4 /*yield*/, (0, src_1.disconnect)()];
-
case 11:
-
_a.sent();
-
console.log('Disconnected from MongoDB.');
-
return [7 /*endfinally*/];
-
case 12: return [2 /*return*/];
-
}
-
});
-
});
-
}
-
runTests();
···
-83
tests/user.test.ts
···
-
import { z } from 'zod';
-
import { defineModel, MongoModel, connect, disconnect, InferModel, InsertType } from '../src';
-
import { ObjectId } from 'mongodb';
-
-
const userSchema = defineModel(z.object({
-
name: z.string(),
-
email: z.string().email(),
-
age: z.number().int().positive().optional(),
-
createdAt: z.date().default(() => new Date()),
-
}));
-
-
type User = InferModel<typeof userSchema>;
-
type UserInsert = InsertType<typeof userSchema>;
-
-
async function runTests() {
-
try {
-
await connect('mongodb://localhost:27017', 'mizzleorm_test_db');
-
console.log('Connected to MongoDB for testing.');
-
-
const UserModel = new MongoModel('users', userSchema);
-
-
// Clean up before tests
-
await UserModel.delete({});
-
console.log('Cleaned up existing data.');
-
-
// Test 1: Insert a new user
-
const newUser: UserInsert = {
-
name: 'Test User',
-
email: 'test@example.com',
-
age: 25,
-
};
-
const insertResult = await UserModel.insertOne(newUser);
-
console.log('Test 1 (Insert): User inserted with ID:', insertResult.insertedId);
-
if (!insertResult.insertedId) {
-
throw new Error('Test 1 Failed: User not inserted.');
-
}
-
-
// Test 2: Find the inserted user
-
const foundUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
-
console.log('Test 2 (Find One): Found user:', foundUser);
-
if (!foundUser || foundUser.email !== 'test@example.com') {
-
throw new Error('Test 2 Failed: User not found or data mismatch.');
-
}
-
-
// Test 3: Update the user
-
const updateResult = await UserModel.update(
-
{ _id: new ObjectId(insertResult.insertedId) },
-
{ age: 26 }
-
);
-
console.log('Test 3 (Update): Modified count:', updateResult.modifiedCount);
-
if (updateResult.modifiedCount !== 1) {
-
throw new Error('Test 3 Failed: User not updated.');
-
}
-
const updatedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
-
if (!updatedUser || updatedUser.age !== 26) {
-
throw new Error('Test 3 Failed: Updated user data mismatch.');
-
}
-
-
// Test 4: Delete the user
-
const deleteResult = await UserModel.delete({ _id: new ObjectId(insertResult.insertedId) });
-
console.log('Test 4 (Delete): Deleted count:', deleteResult.deletedCount);
-
if (deleteResult.deletedCount !== 1) {
-
throw new Error('Test 4 Failed: User not deleted.');
-
}
-
const deletedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
-
if (deletedUser) {
-
throw new Error('Test 4 Failed: User still exists after deletion.');
-
}
-
-
console.log('\nAll tests passed successfully!');
-
-
} catch (error) {
-
console.error('\nTests failed:', error);
-
process.exit(1);
-
} finally {
-
await disconnect();
-
console.log('Disconnected from MongoDB.');
-
}
-
}
-
-
runTests();
-
-
···
-17
tsconfig.json
···
-
{
-
"compilerOptions": {
-
"target": "ES2020",
-
"module": "CommonJS",
-
"lib": ["ES2020", "DOM"],
-
"strict": true,
-
"esModuleInterop": true,
-
"skipLibCheck": true,
-
"forceConsistentCasingInFileNames": true,
-
"outDir": "./dist",
-
"declaration": true
-
},
-
"include": ["src/**/*.ts"],
-
"exclude": ["node_modules", "dist"]
-
}
-
-
···