.DS_Store
.DS_Store
This is a binary file and will not be displayed.
+47
-33
README.md
+47
-33
README.md
···
1
-
# **mizzleORM**
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
-
5
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
8
## ✨ Features
9
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.
15
16
---
17
···
23
yarn add mizzleorm mongodb zod
24
```
25
26
-
> If you need to upgrade your local MongoDB server, see: https://www.mongodb.com/docs/manual/administration/install-community/
27
28
---
29
···
33
34
```ts
35
// src/schemas/user.ts
36
-
import { z } from 'zod';
37
-
import { defineModel } from 'mizzleorm';
38
39
export const userSchema = defineModel(z.object({
40
name: z.string(),
···
52
53
```ts
54
// 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
58
59
type User = InferModel<typeof userSchema>;
60
type UserInsert = InsertType<typeof userSchema>;
61
62
async function main() {
63
// Use the latest connection string format and options
64
-
await connect('mongodb://localhost:27017', 'your_database_name');
65
-
const UserModel = new MongoModel('users', userSchema);
66
67
// Your operations go here
68
···
79
```ts
80
// Insert one
81
const newUser: UserInsert = {
82
-
name: 'John Doe',
83
-
email: 'john.doe@example.com',
84
age: 30,
85
};
86
const insertResult = await UserModel.insertOne(newUser);
87
88
// Find many
89
-
const users = await UserModel.find({ name: 'John Doe' });
90
91
// Find one
92
-
const found = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) }); // ObjectId from mongodb v6+
93
94
// Update
95
-
await UserModel.update({ name: 'John Doe' }, { age: 31 });
96
97
// Delete
98
-
await UserModel.delete({ name: 'John Doe' });
99
100
// Insert many
101
await UserModel.insertMany([
102
-
{ name: 'Alice', email: 'alice@example.com', age: 25 },
103
-
{ name: 'Bob', email: 'bob@example.com' },
104
]);
105
106
// Find by ID
107
await UserModel.findById(insertResult.insertedId);
108
109
// Update one
110
-
await UserModel.updateOne({ name: 'Alice' }, { age: 26 });
111
112
// Replace one
113
-
await UserModel.replaceOne({ name: 'Bob' }, {
114
-
name: 'Bob',
115
-
email: 'bob@newmail.com',
116
age: 22,
117
});
118
119
// Delete one
120
-
await UserModel.deleteOne({ name: 'Alice' });
121
122
// Count
123
const count = await UserModel.count({ age: { $gte: 18 } });
···
125
// Aggregation
126
const aggregation = await UserModel.aggregate([
127
{ $match: { age: { $gte: 18 } } },
128
-
{ $group: { _id: null, avgAge: { $avg: '$age' } } },
129
]);
130
131
// Paginated query
132
const paginated = await UserModel.findPaginated(
133
{ age: { $gte: 18 } },
134
-
{ skip: 0, limit: 10, sort: { age: -1 } }
135
);
136
```
137
···
176
177
MIT — use it freely and contribute back if you'd like!
178
179
-
---
···
1
+
# **Nozzle**
2
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.
6
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+).
10
11
## ✨ Features
12
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.
20
21
---
22
···
28
yarn add mizzleorm mongodb zod
29
```
30
31
+
> If you need to upgrade your local MongoDB server, see:
32
+
> https://www.mongodb.com/docs/manual/administration/install-community/
33
34
---
35
···
39
40
```ts
41
// src/schemas/user.ts
42
+
import { z } from "zod";
43
+
import { defineModel } from "mizzleorm";
44
45
export const userSchema = defineModel(z.object({
46
name: z.string(),
···
58
59
```ts
60
// src/index.ts
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
70
71
type User = InferModel<typeof userSchema>;
72
type UserInsert = InsertType<typeof userSchema>;
73
74
async function main() {
75
// Use the latest connection string format and options
76
+
await connect("mongodb://localhost:27017", "your_database_name");
77
+
const UserModel = new MongoModel("users", userSchema);
78
79
// Your operations go here
80
···
91
```ts
92
// Insert one
93
const newUser: UserInsert = {
94
+
name: "John Doe",
95
+
email: "john.doe@example.com",
96
age: 30,
97
};
98
const insertResult = await UserModel.insertOne(newUser);
99
100
// Find many
101
+
const users = await UserModel.find({ name: "John Doe" });
102
103
// Find one
104
+
const found = await UserModel.findOne({
105
+
_id: new ObjectId(insertResult.insertedId),
106
+
}); // ObjectId from mongodb v6+
107
108
// Update
109
+
await UserModel.update({ name: "John Doe" }, { age: 31 });
110
111
// Delete
112
+
await UserModel.delete({ name: "John Doe" });
113
114
// Insert many
115
await UserModel.insertMany([
116
+
{ name: "Alice", email: "alice@example.com", age: 25 },
117
+
{ name: "Bob", email: "bob@example.com" },
118
]);
119
120
// Find by ID
121
await UserModel.findById(insertResult.insertedId);
122
123
// Update one
124
+
await UserModel.updateOne({ name: "Alice" }, { age: 26 });
125
126
// Replace one
127
+
await UserModel.replaceOne({ name: "Bob" }, {
128
+
name: "Bob",
129
+
email: "bob@newmail.com",
130
age: 22,
131
});
132
133
// Delete one
134
+
await UserModel.deleteOne({ name: "Alice" });
135
136
// Count
137
const count = await UserModel.count({ age: { $gte: 18 } });
···
139
// Aggregation
140
const aggregation = await UserModel.aggregate([
141
{ $match: { age: { $gte: 18 } } },
142
+
{ $group: { _id: null, avgAge: { $avg: "$age" } } },
143
]);
144
145
// Paginated query
146
const paginated = await UserModel.findPaginated(
147
{ age: { $gte: 18 } },
148
+
{ skip: 0, limit: 10, sort: { age: -1 } },
149
);
150
```
151
···
190
191
MIT — use it freely and contribute back if you'd like!
192
193
+
---
+18
deno.json
+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
+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
+220
-97
examples/user.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");
···
41
var mongodb_1 = require("mongodb");
42
// 1. Define your schema using Zod
43
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(); }),
48
}));
49
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
-
});
113
});
114
}
115
runExample();
···
1
"use strict";
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
+
}
9
return new (P || (P = Promise))(function (resolve, reject) {
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());
30
});
31
+
};
32
var __generator = (this && this.__generator) || function (thisArg, body) {
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;
112
}
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
+
}
124
}
125
+
if (op[0] & 5) throw op[1];
126
+
return { value: op[0] ? op[1] : void 0, done: true };
127
+
}
128
};
129
Object.defineProperty(exports, "__esModule", { value: true });
130
var zod_1 = require("zod");
···
132
var mongodb_1 = require("mongodb");
133
// 1. Define your schema using Zod
134
var userSchema = (0, src_1.defineModel)(zod_1.z.object({
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
+
}),
141
}));
142
function runExample() {
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
+
}
235
});
236
+
});
237
}
238
runExample();
+32
-26
examples/user.ts
+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';
4
5
// 1. Define your schema using Zod
6
const userSchema = defineModel(z.object({
···
17
async function runExample() {
18
try {
19
// 3. Connect to MongoDB
20
-
await connect('mongodb://localhost:27017', 'mizzleorm_example');
21
-
console.log('Connected to MongoDB');
22
23
// 2. Create a MongoModel for your collection
24
-
const UserModel = new MongoModel('users', userSchema);
25
26
// Clean up previous data
27
await UserModel.delete({});
28
29
// 4. Insert a new document
30
const newUser: UserInsert = {
31
-
name: 'Alice Smith',
32
-
email: 'alice@example.com',
33
age: 30,
34
};
35
const insertResult = await UserModel.insertOne(newUser);
36
-
console.log('Inserted user:', insertResult.insertedId);
37
38
// 5. Find documents
39
-
const users = await UserModel.find({ name: 'Alice Smith' });
40
-
console.log('Found users:', users);
41
42
// 6. Find one document
43
-
const foundUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
44
-
console.log('Found one user:', foundUser);
45
46
// 7. Update a document
47
const updateResult = await UserModel.update(
48
{ _id: new ObjectId(insertResult.insertedId) },
49
-
{ age: 31 }
50
);
51
-
console.log('Updated user count:', updateResult.modifiedCount);
52
53
-
const updatedUser = await UserModel.findOne({ _id: new ObjectId(insertResult.insertedId) });
54
-
console.log('Updated user data:', updatedUser);
55
56
// 8. Delete documents
57
-
const deleteResult = await UserModel.delete({ name: 'Alice Smith' });
58
-
console.log('Deleted user count:', deleteResult.deletedCount);
59
-
60
} catch (error) {
61
-
console.error('Error during example run:', error);
62
} finally {
63
// 9. Disconnect from MongoDB
64
await disconnect();
65
-
console.log('Disconnected from MongoDB');
66
}
67
}
68
69
runExample();
70
-
71
-
72
-
73
-
···
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";
11
12
// 1. Define your schema using Zod
13
const userSchema = defineModel(z.object({
···
24
async function runExample() {
25
try {
26
// 3. Connect to MongoDB
27
+
await connect("mongodb://localhost:27017", "mizzleorm_example");
28
+
console.log("Connected to MongoDB");
29
30
// 2. Create a MongoModel for your collection
31
+
const UserModel = new MongoModel("users", userSchema);
32
33
// Clean up previous data
34
await UserModel.delete({});
35
36
// 4. Insert a new document
37
const newUser: UserInsert = {
38
+
name: "Alice Smith",
39
+
email: "alice@example.com",
40
age: 30,
41
};
42
const insertResult = await UserModel.insertOne(newUser);
43
+
console.log("Inserted user:", insertResult.insertedId);
44
45
// 5. Find documents
46
+
const users = await UserModel.find({ name: "Alice Smith" });
47
+
console.log("Found users:", users);
48
49
// 6. Find one document
50
+
const foundUser = await UserModel.findOne({
51
+
_id: new ObjectId(insertResult.insertedId),
52
+
});
53
+
console.log("Found one user:", foundUser);
54
55
// 7. Update a document
56
const updateResult = await UserModel.update(
57
{ _id: new ObjectId(insertResult.insertedId) },
58
+
{ age: 31 },
59
);
60
+
console.log("Updated user count:", updateResult.modifiedCount);
61
62
+
const updatedUser = await UserModel.findOne({
63
+
_id: new ObjectId(insertResult.insertedId),
64
+
});
65
+
console.log("Updated user data:", updatedUser);
66
67
// 8. Delete documents
68
+
const deleteResult = await UserModel.delete({ name: "Alice Smith" });
69
+
console.log("Deleted user count:", deleteResult.deletedCount);
70
} catch (error) {
71
+
console.error("Error during example run:", error);
72
} finally {
73
// 9. Disconnect from MongoDB
74
await disconnect();
75
+
console.log("Disconnected from MongoDB");
76
}
77
}
78
79
runExample();
+3
mod.ts
+3
mod.ts
+108
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
-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
-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
+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
+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
-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
+8
-7
src/client.ts
client.ts
···
1
-
import { MongoClient, Db } from 'mongodb';
2
3
-
interface MongoConnection {
4
client: MongoClient;
5
db: Db;
6
}
7
8
-
let connection: MongoConnection | null = null;
9
10
-
export async function connect(uri: string, dbName: string): Promise<MongoConnection> {
11
if (connection) {
12
return connection;
13
}
···
29
30
export function getDb(): Db {
31
if (!connection) {
32
-
throw new Error('MongoDB not connected. Call connect() first.');
33
}
34
return connection.db;
35
}
36
-
37
-
···
1
+
import { type Db, MongoClient } from "mongodb";
2
3
+
interface Connection {
4
client: MongoClient;
5
db: Db;
6
}
7
8
+
let connection: Connection | null = null;
9
10
+
export async function connect(
11
+
uri: string,
12
+
dbName: string,
13
+
): Promise<Connection> {
14
if (connection) {
15
return connection;
16
}
···
32
33
export function getDb(): Db {
34
if (!connection) {
35
+
throw new Error("MongoDB not connected. Call connect() first.");
36
}
37
return connection.db;
38
}
-10
src/index.js
-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
-5
src/index.ts
-73
src/model.js
-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
-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
-6
src/schema.js
-12
src/schema.ts
-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
+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
+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
-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
-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
-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
-
···