Compare commits

..

10 Commits

Author SHA1 Message Date
abd3458606 stuff 2026-05-07 16:40:27 +08:00
8841b571c9 update page title to use app name 2026-05-05 17:22:37 +08:00
cf2df504b1 add task delete modal and alert 2026-05-05 17:00:42 +08:00
584ef9cbb7 changes 2026-05-05 16:40:41 +08:00
597df4e886 Enhance API documentation and error handling in task routes 2026-05-05 16:40:41 +08:00
Tim Basten
63070fd90c feat: Enhance dashboard with stats cards and task filtering
- Added stats cards to display active, completed, overdue, and due-today tasks on the dashboard.
- Implemented search functionality to filter tasks in real-time based on user input.
- Introduced quick-pick date buttons for easier task date selection.
- Updated task rendering logic to handle empty states for task lists.
- Improved overall user interface with new CSS styles for stat cards and buttons.

chore: Update environment variables and backend error handling

- Fixed formatting in the .env file for consistency.
- Enhanced error handling in the backend API for better debugging.

feat: Revamp frontend pages with new features and pricing sections

- Redesigned the index.html page to include a hero section and feature highlights.
- Created a new features page with dynamic loading of features from JSON.
- Implemented a pricing page that loads plans from JSON and highlights the featured plan.
- Added support and FAQ sections with dynamic content loading and contact form functionality.
- Introduced JSON files for FAQs, features, and pricing to allow easy updates without code changes.
2026-04-23 16:29:52 +08:00
e50a6c91e6 a 2026-04-21 15:45:25 +08:00
a49eb4e77c more changes 2026-04-21 15:44:34 +08:00
2a7eea5733 a 2026-04-02 17:50:26 +08:00
ce0aa4f789 a 2026-04-02 17:50:10 +08:00
390 changed files with 83699 additions and 397 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
backend\node_modules
backend/node_modules
backend/.env

BIN
Documentation.docx Normal file

Binary file not shown.

BIN
Documentation.pdf Normal file

Binary file not shown.

View File

@@ -1,2 +1,3 @@
PORT = 3001
MONGO_URI = "mongodb+srv://timmybee:EMqczsTr1oYPBtCd@cluster0.dombm2a.mongodb.net/?appName=Cluster0"
MONGO_URI = "mongodb+srv://timmybee:EMqczsTr1oYPBt@cluster0.dombm2a.mongodb.net/?appName=Cluster0"
APP_URI = "http://localhost:3001"

View File

@@ -1,88 +1,184 @@
require("dotenv").config();
const express = require('express');
const mongoose = require("mongoose")
const cors = require('cors');
const port = 3001;
const app = express();
app.use(express.json());
app.use(cors());
app.use(cors("*"));
app.get("/test", async (req, res) => {
res.send("Hello World!");
});
const tasks = [
{
id: 1,
completed: false,
title: "Buy groceries for the week",
description: "Pick up vegetables, fruits, milk, eggs, and bread from the supermarket.",
dueDate: "14/03/2026",
dateCreated: new Date(Date.now()).toLocaleDateString("en-AU")
},
{
id: 2,
completed: false,
title: "Clean the apartment",
description: "Vacuum the floors, wipe kitchen surfaces, and take out the trash.",
dueDate: "16/03/2026",
dateCreated: new Date(Date.now()).toLocaleDateString("en-AU")
},
{
id: 3,
completed: false,
title: "Call the dentist for appointment",
description: "Book a routine checkup and confirm available time slots for next week.",
dueDate: "17/03/2026",
dateCreated: new Date(Date.now()).toLocaleDateString("en-AU")
},
{
id: 4,
completed: true,
title: "Do laundry and fold clothes",
description: "Wash dark and light loads separately, then fold and organize clean clothes.",
dueDate: "21/03/2026",
dateCreated: new Date(Date.now()).toLocaleDateString("en-AU")
}
];
app.get("/api/tasks", async (req, res) => {
(async () => {
try {
res.json(tasks);
}
catch (error) {
console.error(`Error: ${error}`)
res.status(500).json({ message: "Error getting tasks" })
}
});
mongoose.set("autoIndex", false);
app.post("/api/tasks/todo", async (req, res) => {
await mongoose.connect(process.env.MONGO_URI);
console.log("✅ MongoDB Connected");
await Task.syncIndexes();
console.log("✅ Indexes created");
app.listen(port, () => {
console.log(`✅ To Do App listening on port ${port}`);
});
} catch (error) {
console.error(`Startup Error! ${error}`);
}
})();
// Task Schema
const taskSchema = new mongoose.Schema({
title: { type: String, required: true },
completed: { type: Boolean, required: true, default: false },
description: { type: String, required: true },
dueDate: { type: Date, required: true },
dateCreated: { type: Date, required: true, default: new Date() }
})
taskSchema.index({ dueDate: 1 })
taskSchema.index({ dateCreated: 1 })
const Task = mongoose.model("Task", taskSchema);
// this api route gets all the tasks from our database.
// if the request included a query string, use that for our sorting.
app.get('/api/tasks', async (req, res) => {
try {
const { title, description, dueDate } = req.body;
const { sortBy } = req.query; // destructs the sortBy from the query string. e.g. ?sortBy=dueDate or ?sortBy=dateCreated
let sortOption = {}; // create an empty sort object for the database query
const newTask = {
id: tasks.length + 1,
completed: false,
title, title,
description, description,
dueDate, dueDate,
dateCreated: new Date(Date.now()).toLocaleDateString("en-AU"),
};
tasks.push(newTask);
res.json(newTask);
res.status(201).json(newTask);
if (sortBy === 'dueDate') {
sortOption = { dueDate: 1 }; // if the sortBy query string is dueDate, set the sortOption to dueDate and set the order to ascending (1)
} else if (sortBy === 'dateCreated') {
sortOption = { dateCreated: 1 }; // if the sortBy query string is dateCreated, set the sortOption to dateCreated and set the order to ascending (1)
}
catch (error) {
console.error(`Error: ${error}`)
res.status(500).json({ message: "Error creating task" })
const tasks = await Task.find({}).sort(sortOption); // fetch all task documents and sort them using sortOption
if (!tasks) {
// return the following if the query failed to return a value
return res.status(404).json({ message: "Tasks not found!" });
}
res.json(tasks); // send a json response with the task objects
} catch (error) {
// if there are any errors, do the following
console.error("Error:", error);
res.status(500).json({ message: "Error grabbing tasks!" });
}
});
// this api route takes post requests to save a new MongoDB document, aka, our task
app.post('/api/tasks/todo', async (req, res) => {
try {
const { title, description, dueDate } = req.body; //destructures the request body
const taskData = { title, description, dueDate }; // creating the object to be used for creating the task
const createTask = new Task(taskData); // instantiates a new Task document/model instance using the taskData object values
const newTask = await createTask.save(); // this saves the new document to MongoDB using the Task model/schema.
res.json({ task: newTask, message: "New task created successfully!" }); // if there were no error above, send a response where the body is an JSON object.
} catch (error) {
// if there are any errors, do the following
console.error("Error:", error);
res.status(500).json({ message: "Error creating the task!" });
}
});
app.listen(port, () => {
console.log(`To Do App listening on port ${port}`);
// this api route marks a task as complete using its id.
app.patch('/api/tasks/complete/:id', async (req, res) => {
try {
const { completed } = req.body; // expected boolean value coming from the frontend
const taskId = req.params.id; // grab the task id from the URL params
const completedTask = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); // update completed status and return updated document
if (!completedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" });
}
// return updated task document and confirmation message
res.json({ task: completedTask, message: "Task set to 'Complete'" });
} catch (error) {
// return 500 if database update fails
console.error("Error:", error);
res.status(500).json({ message: "Error completing the task!" });
}
});
// this api route marks a task as not complete using its id.
app.patch('/api/tasks/notComplete/:id', async (req, res) => {
try {
const { completed } = req.body; // expected boolean value coming from the frontend
const taskId = req.params.id; // grab the task id from the URL params
const taskNotComplete = await Task.findByIdAndUpdate(taskId, { completed }, { new: true }); // update completed status and return updated document
if (!taskNotComplete) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" });
}
// return updated task document and confirmation message
res.json({ task: taskNotComplete, message: "Task set to 'Not Complete'" });
} catch (error) {
// return 500 if database update fails
console.error("Error:", error);
res.status(500).json({ message: "Error making the task NOT complete!" });
}
});
// this api route deletes a task by id.
app.delete('/api/tasks/delete/:id', async (req, res) => {
try {
const taskId = req.params.id; // grab the task id from the URL params
const deletedTask = await Task.findByIdAndDelete(taskId); // delete the matching task document
if (!deletedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" });
}
// return deleted task document and confirmation message
res.json({ task: deletedTask, message: "Task deleted successfully!" });
} catch (error) {
// return 500 if deletion fails
console.error("Error:", error);
res.status(500).json({ message: "Error deleting the task!" });
}
});
// this api route updates a task's editable fields by id.
app.put('/api/tasks/update/:id', async (req, res) => {
try {
const taskId = req.params.id; // grab the task id from the URL params
const { title, description, dueDate } = req.body; // extract updated fields from request body
const taskData = { title, description, dueDate }; // build the update object
const updatedTask = await Task.findByIdAndUpdate(taskId, taskData, { new: true }); // apply update and return updated document
if (!updatedTask) {
// return 404 if no task matches the provided id
return res.status(404).json({ message: "Task not found!" });
}
// return updated task document and confirmation message
res.json({ task: updatedTask, message: "Task updated successfully!" });
} catch (error) {
// return 500 if update fails
console.error('Error:', error);
res.status(500).json({ message: "Error updating the task!" });
}
});

View File

@@ -502,6 +502,15 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
"node_modules/kareem": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-3.2.0.tgz",
"integrity": "sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==",
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -622,6 +631,45 @@
"node": ">=20.19.0"
}
},
"node_modules/mongoose": {
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.3.3.tgz",
"integrity": "sha512-sfv5LOIPWeN5o/281kp4Rx9ZnuXb0g8CtvBTi7trYQs2PYYx8LWXegXxG3ar7VEns1o+d4h9LI/Dtc7dTTyYmA==",
"license": "MIT",
"dependencies": {
"kareem": "3.2.0",
"mongodb": "~7.1",
"mpath": "0.9.0",
"mquery": "6.0.0",
"ms": "2.1.3",
"sift": "17.1.3"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mongoose"
}
},
"node_modules/mpath": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/mquery": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz",
"integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==",
"license": "MIT",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -904,6 +952,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sift": {
"version": "17.1.3",
"resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
"license": "MIT"
},
"node_modules/sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"parent":null,"pid":2678345,"argv":["/home/v/.nvm/versions/node/v20.18.1/bin/node","/home/v/Desktop/MongoDB/kareem/node_modules/.bin/mocha","./test/examples.test.js","./test/misc.test.js","./test/post.test.js","./test/pre.test.js","./test/wrap.test.js"],"execArgv":[],"cwd":"/home/v/Desktop/MongoDB/kareem","time":1756153005026,"ppid":2678334,"coverageFilename":"/home/v/Desktop/MongoDB/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json","externalId":"","uuid":"fa57d6c4-9e78-4624-9229-f77b87a07481","files":["/home/v/Desktop/MongoDB/kareem/index.js"]}

View File

@@ -0,0 +1 @@
{"processes":{"fa57d6c4-9e78-4624-9229-f77b87a07481":{"parent":null,"children":[]}},"files":{"/home/v/Desktop/MongoDB/kareem/index.js":["fa57d6c4-9e78-4624-9229-f77b87a07481"]},"externalIds":{}}

840
backend/node_modules/kareem/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,840 @@
# Changelog
<a name="3.2.0"></a>
## 3.2.0 (2026-01-29)
* feat(exec): add filter option to execPreSync and execPostSync #44
<a name="3.1.0"></a>
## 3.1.0 (2026-01-12)
* feat(exec): add filter option to allow executing hooks based on a filter function #43
<a name="3.0.0"></a>
## 3.0.0 (2025-11-18)
* BREAKING CHANGE: make execPre async and drop callback support #39
* BREAKING CHANGE: require Node 18
* feat: overwriteArguments support #42
<a name="2.6.0"></a>
## 2.6.0 (2024-03-04)
* feat: add TypeScript types
<a name="2.5.1"></a>
## 2.5.1 (2023-01-06)
* fix: avoid passing final callback to pre hook, because calling the callback can mess up hook execution #36 Automattic/mongoose#12836
<a name="2.5.0"></a>
## 2.5.0 (2022-12-01)
* feat: add errorHandler option to `post()` #34
<a name="2.4.0"></a>
## 2.4.0 (2022-06-13)
* feat: add `overwriteResult()` and `skipWrappedFunction()` for more advanced control flow
<a name="2.3.4"></a>
## 2.3.4 (2022-02-10)
* perf: various performance improvements #27 #24 #23 #22 #21 #20
<a name="2.3.3"></a>
## 2.3.3 (2021-12-26)
* fix: handle sync errors in `wrap()`
<a name="2.3.2"></a>
## 2.3.2 (2020-12-08)
* fix: handle sync errors in pre hooks if there are multiple hooks
<a name="2.3.0"></a>
## 2.3.0 (2018-09-24)
* chore(release): 2.2.3 ([c8f2695](https://github.com/vkarpov15/kareem/commit/c8f2695))
* chore(release): 2.2.4 ([a377a4f](https://github.com/vkarpov15/kareem/commit/a377a4f))
* chore(release): 2.2.5 ([5a495e3](https://github.com/vkarpov15/kareem/commit/5a495e3))
* fix(filter): copy async pres correctly with `filter()` ([1b1ed8a](https://github.com/vkarpov15/kareem/commit/1b1ed8a)), closes [Automattic/mongoose#3054](https://github.com/Automattic/mongoose/issues/3054)
* feat: add filter() function ([1f641f4](https://github.com/vkarpov15/kareem/commit/1f641f4))
* feat: support storing options on pre and post hooks ([59220b9](https://github.com/vkarpov15/kareem/commit/59220b9))
<a name="2.2.3"></a>
## <small>2.2.3 (2018-09-10)</small>
* chore: release 2.2.3 ([af653a3](https://github.com/vkarpov15/kareem/commit/af653a3))
<a name="2.2.2"></a>
## <small>2.2.2 (2018-09-10)</small>
* chore: release 2.2.2 ([3f0144d](https://github.com/vkarpov15/kareem/commit/3f0144d))
* fix: allow merge() to not clone ([e628d65](https://github.com/vkarpov15/kareem/commit/e628d65))
<a name="2.2.1"></a>
## <small>2.2.1 (2018-06-05)</small>
* chore: release 2.2.1 ([4625a64](https://github.com/vkarpov15/kareem/commit/4625a64))
* chore: remove lockfile from git ([7f3e4e6](https://github.com/vkarpov15/kareem/commit/7f3e4e6))
* fix: handle numAsync correctly when merging ([fef8e7e](https://github.com/vkarpov15/kareem/commit/fef8e7e))
* test: repro issue with not copying numAsync ([952d9db](https://github.com/vkarpov15/kareem/commit/952d9db))
<a name="2.2.0"></a>
## 2.2.0 (2018-06-05)
* chore: release 2.2.0 ([ff9ad03](https://github.com/vkarpov15/kareem/commit/ff9ad03))
* fix: use maps instead of objects for _pres and _posts so `toString()` doesn't get reported as having ([55df303](https://github.com/vkarpov15/kareem/commit/55df303)), closes [Automattic/mongoose#6538](https://github.com/Automattic/mongoose/issues/6538)
<a name="2.1.0"></a>
## 2.1.0 (2018-05-16)
* chore: release 2.1.0 ([ba5f1bc](https://github.com/vkarpov15/kareem/commit/ba5f1bc))
* feat: add option to check wrapped function return value for promises ([c9d7dd1](https://github.com/vkarpov15/kareem/commit/c9d7dd1))
* refactor: use const in wrap() ([0fc21f9](https://github.com/vkarpov15/kareem/commit/0fc21f9))
<a name="2.0.7"></a>
## <small>2.0.7 (2018-04-28)</small>
* chore: release 2.0.7 ([0bf91e6](https://github.com/vkarpov15/kareem/commit/0bf91e6))
* feat: add `hasHooks()` ([225f18d](https://github.com/vkarpov15/kareem/commit/225f18d)), closes [Automattic/mongoose#6385](https://github.com/Automattic/mongoose/issues/6385)
<a name="2.0.6"></a>
## <small>2.0.6 (2018-03-22)</small>
* chore: release 2.0.6 ([f3d406b](https://github.com/vkarpov15/kareem/commit/f3d406b))
* fix(wrap): ensure fast path still wraps function in `nextTick()` for chaining ([7000494](https://github.com/vkarpov15/kareem/commit/7000494)), closes [Automattic/mongoose#6250](https://github.com/Automattic/mongoose/issues/6250) [dsanel/mongoose-delete#36](https://github.com/dsanel/mongoose-delete/issues/36)
<a name="2.0.5"></a>
## <small>2.0.5 (2018-02-22)</small>
* chore: release 2.0.5 ([3286612](https://github.com/vkarpov15/kareem/commit/3286612))
* perf(createWrapper): don't create wrapper if there are no hooks ([5afc5b9](https://github.com/vkarpov15/kareem/commit/5afc5b9)), closes [Automattic/mongoose#6126](https://github.com/Automattic/mongoose/issues/6126)
<a name="2.0.4"></a>
## <small>2.0.4 (2018-02-08)</small>
* chore: release 2.0.4 ([2ab0293](https://github.com/vkarpov15/kareem/commit/2ab0293))
<a name="2.0.3"></a>
## <small>2.0.3 (2018-02-01)</small>
* chore: release 2.0.3 ([3c1abe5](https://github.com/vkarpov15/kareem/commit/3c1abe5))
* fix: use process.nextTick() re: Automattic/mongoose#6074 ([e5bfe33](https://github.com/vkarpov15/kareem/commit/e5bfe33)), closes [Automattic/mongoose#6074](https://github.com/Automattic/mongoose/issues/6074)
<a name="2.0.2"></a>
## <small>2.0.2 (2018-01-24)</small>
* chore: fix license ([a9d755c](https://github.com/vkarpov15/kareem/commit/a9d755c)), closes [#10](https://github.com/vkarpov15/kareem/issues/10)
* chore: release 2.0.2 ([fe87ab6](https://github.com/vkarpov15/kareem/commit/fe87ab6))
<a name="2.0.1"></a>
## <small>2.0.1 (2018-01-09)</small>
* chore: release 2.0.1 with lockfile bump ([09c44fb](https://github.com/vkarpov15/kareem/commit/09c44fb))
<a name="2.0.0"></a>
## 2.0.0 (2018-01-09)
* chore: bump marked re: security ([cc564a9](https://github.com/vkarpov15/kareem/commit/cc564a9))
* chore: release 2.0.0 ([f511d1c](https://github.com/vkarpov15/kareem/commit/f511d1c))
<a name="2.0.0-rc5"></a>
## 2.0.0-rc5 (2017-12-23)
* chore: fix build on node 4+5 ([6dac5a4](https://github.com/vkarpov15/kareem/commit/6dac5a4))
* chore: fix built on node 4 + 5 again ([434ef0a](https://github.com/vkarpov15/kareem/commit/434ef0a))
* chore: release 2.0.0-rc5 ([25a32ee](https://github.com/vkarpov15/kareem/commit/25a32ee))
<a name="2.0.0-rc4"></a>
## 2.0.0-rc4 (2017-12-22)
* chore: release 2.0.0-rc4 ([49fc083](https://github.com/vkarpov15/kareem/commit/49fc083))
* BREAKING CHANGE: deduplicate when merging hooks re: Automattic/mongoose#2945 ([d458573](https://github.com/vkarpov15/kareem/commit/d458573)), closes [Automattic/mongoose#2945](https://github.com/Automattic/mongoose/issues/2945)
<a name="2.0.0-rc3"></a>
## 2.0.0-rc3 (2017-12-22)
* chore: release 2.0.0-rc3 ([adaaa00](https://github.com/vkarpov15/kareem/commit/adaaa00))
* feat: support returning promises from middleware functions ([05b4480](https://github.com/vkarpov15/kareem/commit/05b4480)), closes [Automattic/mongoose#3779](https://github.com/Automattic/mongoose/issues/3779)
<a name="2.0.0-rc2"></a>
## 2.0.0-rc2 (2017-12-21)
* chore: release 2.0.0-rc2 ([76325fa](https://github.com/vkarpov15/kareem/commit/76325fa))
* fix: ensure next() and done() run in next tick ([6c20684](https://github.com/vkarpov15/kareem/commit/6c20684))
<a name="2.0.0-rc1"></a>
## 2.0.0-rc1 (2017-12-21)
* chore: improve test coverage re: Automattic/mongoose#3232 ([7b45cf0](https://github.com/vkarpov15/kareem/commit/7b45cf0)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232)
* chore: release 2.0.0-rc1 ([9b83f52](https://github.com/vkarpov15/kareem/commit/9b83f52))
* BREAKING CHANGE: report sync exceptions as errors, only allow calling next() and done() once ([674adcc](https://github.com/vkarpov15/kareem/commit/674adcc)), closes [Automattic/mongoose#3483](https://github.com/Automattic/mongoose/issues/3483)
<a name="2.0.0-rc0"></a>
## 2.0.0-rc0 (2017-12-17)
* chore: release 2.0.0-rc0 ([16b44b5](https://github.com/vkarpov15/kareem/commit/16b44b5))
* BREAKING CHANGE: drop support for node < 4 ([9cbb8c7](https://github.com/vkarpov15/kareem/commit/9cbb8c7))
* BREAKING CHANGE: remove useLegacyPost and add several new features ([6dd8531](https://github.com/vkarpov15/kareem/commit/6dd8531)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232)
<a name="1.5.0"></a>
## 1.5.0 (2017-07-20)
* chore: release 1.5.0 ([9c491a0](https://github.com/vkarpov15/kareem/commit/9c491a0))
* fix: improve post error handlers results ([9928dd5](https://github.com/vkarpov15/kareem/commit/9928dd5)), closes [Automattic/mongoose#5466](https://github.com/Automattic/mongoose/issues/5466)
<a name="1.4.2"></a>
## <small>1.4.2 (2017-07-06)</small>
* chore: release 1.4.2 ([8d14ac5](https://github.com/vkarpov15/kareem/commit/8d14ac5))
* fix: correct args re: Automattic/mongoose#5405 ([3f28ae6](https://github.com/vkarpov15/kareem/commit/3f28ae6)), closes [Automattic/mongoose#5405](https://github.com/Automattic/mongoose/issues/5405)
<a name="1.4.1"></a>
## <small>1.4.1 (2017-04-25)</small>
* chore: release 1.4.1 ([5ecf0c2](https://github.com/vkarpov15/kareem/commit/5ecf0c2))
* fix: handle numAsyncPres with clone() ([c72e857](https://github.com/vkarpov15/kareem/commit/c72e857)), closes [#8](https://github.com/vkarpov15/kareem/issues/8)
* test: repro #8 ([9b4d6b2](https://github.com/vkarpov15/kareem/commit/9b4d6b2)), closes [#8](https://github.com/vkarpov15/kareem/issues/8)
<a name="1.4.0"></a>
## 1.4.0 (2017-04-19)
* chore: release 1.4.0 ([101c5f5](https://github.com/vkarpov15/kareem/commit/101c5f5))
* feat: add merge() function ([285325e](https://github.com/vkarpov15/kareem/commit/285325e))
<a name="1.3.0"></a>
## 1.3.0 (2017-03-26)
* chore: release 1.3.0 ([f3a9e50](https://github.com/vkarpov15/kareem/commit/f3a9e50))
* feat: pass function args to execPre ([4dd466d](https://github.com/vkarpov15/kareem/commit/4dd466d))
<a name="1.2.1"></a>
## <small>1.2.1 (2017-02-03)</small>
* chore: release 1.2.1 ([d97081f](https://github.com/vkarpov15/kareem/commit/d97081f))
* fix: filter out _kareemIgnored args for error handlers re: Automattic/mongoose#4925 ([ddc7aeb](https://github.com/vkarpov15/kareem/commit/ddc7aeb)), closes [Automattic/mongoose#4925](https://github.com/Automattic/mongoose/issues/4925)
* fix: make error handlers handle errors in pre hooks ([af38033](https://github.com/vkarpov15/kareem/commit/af38033)), closes [Automattic/mongoose#4927](https://github.com/Automattic/mongoose/issues/4927)
<a name="1.2.0"></a>
## 1.2.0 (2017-01-02)
* chore: release 1.2.0 ([033225c](https://github.com/vkarpov15/kareem/commit/033225c))
* chore: upgrade deps ([f9e9a09](https://github.com/vkarpov15/kareem/commit/f9e9a09))
* feat: add _kareemIgnore re: Automattic/mongoose#4836 ([7957771](https://github.com/vkarpov15/kareem/commit/7957771)), closes [Automattic/mongoose#4836](https://github.com/Automattic/mongoose/issues/4836)
<a name="1.1.5"></a>
## <small>1.1.5 (2016-12-13)</small>
* chore: release 1.1.5 ([1a9f684](https://github.com/vkarpov15/kareem/commit/1a9f684))
* fix: correct field name ([04a0e9d](https://github.com/vkarpov15/kareem/commit/04a0e9d))
<a name="1.1.4"></a>
## <small>1.1.4 (2016-12-09)</small>
* chore: release 1.1.4 ([ece401c](https://github.com/vkarpov15/kareem/commit/ece401c))
* chore: run tests on node 6 ([e0cb1cb](https://github.com/vkarpov15/kareem/commit/e0cb1cb))
* fix: only copy own properties in clone() ([dfe28ce](https://github.com/vkarpov15/kareem/commit/dfe28ce)), closes [#7](https://github.com/vkarpov15/kareem/issues/7)
<a name="1.1.3"></a>
## <small>1.1.3 (2016-06-27)</small>
* chore: release 1.1.3 ([87171c8](https://github.com/vkarpov15/kareem/commit/87171c8))
* fix: couple more issues with arg processing ([c65f523](https://github.com/vkarpov15/kareem/commit/c65f523))
<a name="1.1.2"></a>
## <small>1.1.2 (2016-06-27)</small>
* chore: release 1.1.2 ([8e102b6](https://github.com/vkarpov15/kareem/commit/8e102b6))
* fix: add early return ([4feda4e](https://github.com/vkarpov15/kareem/commit/4feda4e))
<a name="1.1.1"></a>
## <small>1.1.1 (2016-06-27)</small>
* chore: release 1.1.1 ([8bb3050](https://github.com/vkarpov15/kareem/commit/8bb3050))
* fix: skip error handlers if no error ([0eb3a44](https://github.com/vkarpov15/kareem/commit/0eb3a44))
<a name="1.1.0"></a>
## 1.1.0 (2016-05-11)
* chore: release 1.1.0 ([85332d9](https://github.com/vkarpov15/kareem/commit/85332d9))
* chore: test on node 4 and node 5 ([1faefa1](https://github.com/vkarpov15/kareem/commit/1faefa1))
* 100% coverage again ([c9aee4e](https://github.com/vkarpov15/kareem/commit/c9aee4e))
* add support for error post hooks ([d378113](https://github.com/vkarpov15/kareem/commit/d378113))
* basic setup for sync hooks #4 ([55aa081](https://github.com/vkarpov15/kareem/commit/55aa081)), closes [#4](https://github.com/vkarpov15/kareem/issues/4)
* proof of concept for error handlers ([e4a07d9](https://github.com/vkarpov15/kareem/commit/e4a07d9))
* refactor out handleWrapError helper ([b19af38](https://github.com/vkarpov15/kareem/commit/b19af38))
<a name="1.0.1"></a>
## <small>1.0.1 (2015-05-10)</small>
* Fix #1 ([de60dc6](https://github.com/vkarpov15/kareem/commit/de60dc6)), closes [#1](https://github.com/vkarpov15/kareem/issues/1)
* release 1.0.1 ([6971088](https://github.com/vkarpov15/kareem/commit/6971088))
* Run tests on iojs in travis ([adcd201](https://github.com/vkarpov15/kareem/commit/adcd201))
* support legacy post hook behavior in wrap() ([23fa74c](https://github.com/vkarpov15/kareem/commit/23fa74c))
* Use node 0.12 in travis ([834689d](https://github.com/vkarpov15/kareem/commit/834689d))
<a name="1.0.0"></a>
## 1.0.0 (2015-01-28)
* Tag 1.0.0 ([4c5a35a](https://github.com/vkarpov15/kareem/commit/4c5a35a))
<a name="0.0.8"></a>
## <small>0.0.8 (2015-01-27)</small>
* Add clone function ([688bba7](https://github.com/vkarpov15/kareem/commit/688bba7))
* Add jscs for style checking ([5c93149](https://github.com/vkarpov15/kareem/commit/5c93149))
* Bump 0.0.8 ([03c0d2f](https://github.com/vkarpov15/kareem/commit/03c0d2f))
* Fix jscs config, add gulp rules ([9989abf](https://github.com/vkarpov15/kareem/commit/9989abf))
* fix Makefile typo ([1f7e61a](https://github.com/vkarpov15/kareem/commit/1f7e61a))
<a name="0.0.7"></a>
## <small>0.0.7 (2015-01-04)</small>
* Bump 0.0.7 ([98ef173](https://github.com/vkarpov15/kareem/commit/98ef173))
* fix LearnBoost/mongoose#2553 - use null instead of undefined for err ([9157b48](https://github.com/vkarpov15/kareem/commit/9157b48)), closes [LearnBoost/mongoose#2553](https://github.com/LearnBoost/mongoose/issues/2553)
* Regenerate docs ([2331cdf](https://github.com/vkarpov15/kareem/commit/2331cdf))
<a name="0.0.6"></a>
## <small>0.0.6 (2015-01-01)</small>
* Update docs and bump 0.0.6 ([92c12a7](https://github.com/vkarpov15/kareem/commit/92c12a7))
<a name="0.0.5"></a>
## <small>0.0.5 (2015-01-01)</small>
* Add coverage rule to Makefile ([825a91c](https://github.com/vkarpov15/kareem/commit/825a91c))
* Add coveralls to README ([fb52369](https://github.com/vkarpov15/kareem/commit/fb52369))
* Add coveralls to travis ([93f6f15](https://github.com/vkarpov15/kareem/commit/93f6f15))
* Add createWrapper() function ([ea77741](https://github.com/vkarpov15/kareem/commit/ea77741))
* Add istanbul code coverage ([6eceeef](https://github.com/vkarpov15/kareem/commit/6eceeef))
* Add some more comments for examples ([c5b0c6f](https://github.com/vkarpov15/kareem/commit/c5b0c6f))
* Add travis ([e6dcb06](https://github.com/vkarpov15/kareem/commit/e6dcb06))
* Add travis badge to docs ([ad8c9b3](https://github.com/vkarpov15/kareem/commit/ad8c9b3))
* Add wrap() tests, 100% coverage ([6945be4](https://github.com/vkarpov15/kareem/commit/6945be4))
* Better test coverage for execPost ([d9ad539](https://github.com/vkarpov15/kareem/commit/d9ad539))
* Bump 0.0.5 ([69875b1](https://github.com/vkarpov15/kareem/commit/69875b1))
* Docs fix ([15b7098](https://github.com/vkarpov15/kareem/commit/15b7098))
* Fix silly mistake in docs generation ([50373eb](https://github.com/vkarpov15/kareem/commit/50373eb))
* Fix typo in readme ([fec4925](https://github.com/vkarpov15/kareem/commit/fec4925))
* Linkify travis badge ([92b25fe](https://github.com/vkarpov15/kareem/commit/92b25fe))
* Make travis run coverage ([747157b](https://github.com/vkarpov15/kareem/commit/747157b))
* Move travis status badge ([d52e89b](https://github.com/vkarpov15/kareem/commit/d52e89b))
* Quick fix for coverage ([50bbddb](https://github.com/vkarpov15/kareem/commit/50bbddb))
* Typo fix ([adea794](https://github.com/vkarpov15/kareem/commit/adea794))
<a name="0.0.4"></a>
## <small>0.0.4 (2014-12-13)</small>
* Bump 0.0.4, run docs generation ([51a15fe](https://github.com/vkarpov15/kareem/commit/51a15fe))
* Use correct post parameters in wrap() ([9bb5da3](https://github.com/vkarpov15/kareem/commit/9bb5da3))
<a name="0.0.3"></a>
## <small>0.0.3 (2014-12-12)</small>
* Add npm test script, fix small bug with args not getting passed through post ([49e3e68](https://github.com/vkarpov15/kareem/commit/49e3e68))
* Bump 0.0.3 ([65621d8](https://github.com/vkarpov15/kareem/commit/65621d8))
* Update readme ([901388b](https://github.com/vkarpov15/kareem/commit/901388b))
<a name="0.0.2"></a>
## <small>0.0.2 (2014-12-12)</small>
* Add github repo and bump 0.0.2 ([59db8be](https://github.com/vkarpov15/kareem/commit/59db8be))
<a name="0.0.1"></a>
## <small>0.0.1 (2014-12-12)</small>
* Add basic docs ([ad29ea4](https://github.com/vkarpov15/kareem/commit/ad29ea4))
* Add pre hooks ([2ffc356](https://github.com/vkarpov15/kareem/commit/2ffc356))
* Add wrap function ([68c540c](https://github.com/vkarpov15/kareem/commit/68c540c))
* Bump to version 0.0.1 ([a4bfd68](https://github.com/vkarpov15/kareem/commit/a4bfd68))
* Initial commit ([4002458](https://github.com/vkarpov15/kareem/commit/4002458))
* Initial deposit ([98fc489](https://github.com/vkarpov15/kareem/commit/98fc489))
* Post hooks ([395b67c](https://github.com/vkarpov15/kareem/commit/395b67c))
* Some basic setup work ([82df75e](https://github.com/vkarpov15/kareem/commit/82df75e))
* Support sync pre hooks ([1cc1b9f](https://github.com/vkarpov15/kareem/commit/1cc1b9f))
* Update package.json description ([978da18](https://github.com/vkarpov15/kareem/commit/978da18))
<a name="2.2.5"></a>
## <small>2.2.5 (2018-09-24)</small>
<a name="2.2.4"></a>
## <small>2.2.4 (2018-09-24)</small>
<a name="2.2.3"></a>
## <small>2.2.3 (2018-09-24)</small>
* fix(filter): copy async pres correctly with `filter()` ([1b1ed8a](https://github.com/vkarpov15/kareem/commit/1b1ed8a)), closes [Automattic/mongoose#3054](https://github.com/Automattic/mongoose/issues/3054)
* feat: add filter() function ([1f641f4](https://github.com/vkarpov15/kareem/commit/1f641f4))
* feat: support storing options on pre and post hooks ([59220b9](https://github.com/vkarpov15/kareem/commit/59220b9))
<a name="2.2.3"></a>
## <small>2.2.3 (2018-09-10)</small>
* chore: release 2.2.3 ([af653a3](https://github.com/vkarpov15/kareem/commit/af653a3))
<a name="2.2.2"></a>
## <small>2.2.2 (2018-09-10)</small>
* chore: release 2.2.2 ([3f0144d](https://github.com/vkarpov15/kareem/commit/3f0144d))
* fix: allow merge() to not clone ([e628d65](https://github.com/vkarpov15/kareem/commit/e628d65))
<a name="2.2.1"></a>
## <small>2.2.1 (2018-06-05)</small>
* chore: release 2.2.1 ([4625a64](https://github.com/vkarpov15/kareem/commit/4625a64))
* chore: remove lockfile from git ([7f3e4e6](https://github.com/vkarpov15/kareem/commit/7f3e4e6))
* fix: handle numAsync correctly when merging ([fef8e7e](https://github.com/vkarpov15/kareem/commit/fef8e7e))
* test: repro issue with not copying numAsync ([952d9db](https://github.com/vkarpov15/kareem/commit/952d9db))
<a name="2.2.0"></a>
## 2.2.0 (2018-06-05)
* chore: release 2.2.0 ([ff9ad03](https://github.com/vkarpov15/kareem/commit/ff9ad03))
* fix: use maps instead of objects for _pres and _posts so `toString()` doesn't get reported as having ([55df303](https://github.com/vkarpov15/kareem/commit/55df303)), closes [Automattic/mongoose#6538](https://github.com/Automattic/mongoose/issues/6538)
<a name="2.1.0"></a>
## 2.1.0 (2018-05-16)
* chore: release 2.1.0 ([ba5f1bc](https://github.com/vkarpov15/kareem/commit/ba5f1bc))
* feat: add option to check wrapped function return value for promises ([c9d7dd1](https://github.com/vkarpov15/kareem/commit/c9d7dd1))
* refactor: use const in wrap() ([0fc21f9](https://github.com/vkarpov15/kareem/commit/0fc21f9))
<a name="2.0.7"></a>
## <small>2.0.7 (2018-04-28)</small>
* chore: release 2.0.7 ([0bf91e6](https://github.com/vkarpov15/kareem/commit/0bf91e6))
* feat: add `hasHooks()` ([225f18d](https://github.com/vkarpov15/kareem/commit/225f18d)), closes [Automattic/mongoose#6385](https://github.com/Automattic/mongoose/issues/6385)
<a name="2.0.6"></a>
## <small>2.0.6 (2018-03-22)</small>
* chore: release 2.0.6 ([f3d406b](https://github.com/vkarpov15/kareem/commit/f3d406b))
* fix(wrap): ensure fast path still wraps function in `nextTick()` for chaining ([7000494](https://github.com/vkarpov15/kareem/commit/7000494)), closes [Automattic/mongoose#6250](https://github.com/Automattic/mongoose/issues/6250) [dsanel/mongoose-delete#36](https://github.com/dsanel/mongoose-delete/issues/36)
<a name="2.0.5"></a>
## <small>2.0.5 (2018-02-22)</small>
* chore: release 2.0.5 ([3286612](https://github.com/vkarpov15/kareem/commit/3286612))
* perf(createWrapper): don't create wrapper if there are no hooks ([5afc5b9](https://github.com/vkarpov15/kareem/commit/5afc5b9)), closes [Automattic/mongoose#6126](https://github.com/Automattic/mongoose/issues/6126)
<a name="2.0.4"></a>
## <small>2.0.4 (2018-02-08)</small>
* chore: release 2.0.4 ([2ab0293](https://github.com/vkarpov15/kareem/commit/2ab0293))
<a name="2.0.3"></a>
## <small>2.0.3 (2018-02-01)</small>
* chore: release 2.0.3 ([3c1abe5](https://github.com/vkarpov15/kareem/commit/3c1abe5))
* fix: use process.nextTick() re: Automattic/mongoose#6074 ([e5bfe33](https://github.com/vkarpov15/kareem/commit/e5bfe33)), closes [Automattic/mongoose#6074](https://github.com/Automattic/mongoose/issues/6074)
<a name="2.0.2"></a>
## <small>2.0.2 (2018-01-24)</small>
* chore: fix license ([a9d755c](https://github.com/vkarpov15/kareem/commit/a9d755c)), closes [#10](https://github.com/vkarpov15/kareem/issues/10)
* chore: release 2.0.2 ([fe87ab6](https://github.com/vkarpov15/kareem/commit/fe87ab6))
<a name="2.0.1"></a>
## <small>2.0.1 (2018-01-09)</small>
* chore: release 2.0.1 with lockfile bump ([09c44fb](https://github.com/vkarpov15/kareem/commit/09c44fb))
<a name="2.0.0"></a>
## 2.0.0 (2018-01-09)
* chore: bump marked re: security ([cc564a9](https://github.com/vkarpov15/kareem/commit/cc564a9))
* chore: release 2.0.0 ([f511d1c](https://github.com/vkarpov15/kareem/commit/f511d1c))
<a name="2.0.0-rc5"></a>
## 2.0.0-rc5 (2017-12-23)
* chore: fix build on node 4+5 ([6dac5a4](https://github.com/vkarpov15/kareem/commit/6dac5a4))
* chore: fix built on node 4 + 5 again ([434ef0a](https://github.com/vkarpov15/kareem/commit/434ef0a))
* chore: release 2.0.0-rc5 ([25a32ee](https://github.com/vkarpov15/kareem/commit/25a32ee))
<a name="2.0.0-rc4"></a>
## 2.0.0-rc4 (2017-12-22)
* chore: release 2.0.0-rc4 ([49fc083](https://github.com/vkarpov15/kareem/commit/49fc083))
* BREAKING CHANGE: deduplicate when merging hooks re: Automattic/mongoose#2945 ([d458573](https://github.com/vkarpov15/kareem/commit/d458573)), closes [Automattic/mongoose#2945](https://github.com/Automattic/mongoose/issues/2945)
<a name="2.0.0-rc3"></a>
## 2.0.0-rc3 (2017-12-22)
* chore: release 2.0.0-rc3 ([adaaa00](https://github.com/vkarpov15/kareem/commit/adaaa00))
* feat: support returning promises from middleware functions ([05b4480](https://github.com/vkarpov15/kareem/commit/05b4480)), closes [Automattic/mongoose#3779](https://github.com/Automattic/mongoose/issues/3779)
<a name="2.0.0-rc2"></a>
## 2.0.0-rc2 (2017-12-21)
* chore: release 2.0.0-rc2 ([76325fa](https://github.com/vkarpov15/kareem/commit/76325fa))
* fix: ensure next() and done() run in next tick ([6c20684](https://github.com/vkarpov15/kareem/commit/6c20684))
<a name="2.0.0-rc1"></a>
## 2.0.0-rc1 (2017-12-21)
* chore: improve test coverage re: Automattic/mongoose#3232 ([7b45cf0](https://github.com/vkarpov15/kareem/commit/7b45cf0)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232)
* chore: release 2.0.0-rc1 ([9b83f52](https://github.com/vkarpov15/kareem/commit/9b83f52))
* BREAKING CHANGE: report sync exceptions as errors, only allow calling next() and done() once ([674adcc](https://github.com/vkarpov15/kareem/commit/674adcc)), closes [Automattic/mongoose#3483](https://github.com/Automattic/mongoose/issues/3483)
<a name="2.0.0-rc0"></a>
## 2.0.0-rc0 (2017-12-17)
* chore: release 2.0.0-rc0 ([16b44b5](https://github.com/vkarpov15/kareem/commit/16b44b5))
* BREAKING CHANGE: drop support for node < 4 ([9cbb8c7](https://github.com/vkarpov15/kareem/commit/9cbb8c7))
* BREAKING CHANGE: remove useLegacyPost and add several new features ([6dd8531](https://github.com/vkarpov15/kareem/commit/6dd8531)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232)
<a name="1.5.0"></a>
## 1.5.0 (2017-07-20)
* chore: release 1.5.0 ([9c491a0](https://github.com/vkarpov15/kareem/commit/9c491a0))
* fix: improve post error handlers results ([9928dd5](https://github.com/vkarpov15/kareem/commit/9928dd5)), closes [Automattic/mongoose#5466](https://github.com/Automattic/mongoose/issues/5466)
<a name="1.4.2"></a>
## <small>1.4.2 (2017-07-06)</small>
* chore: release 1.4.2 ([8d14ac5](https://github.com/vkarpov15/kareem/commit/8d14ac5))
* fix: correct args re: Automattic/mongoose#5405 ([3f28ae6](https://github.com/vkarpov15/kareem/commit/3f28ae6)), closes [Automattic/mongoose#5405](https://github.com/Automattic/mongoose/issues/5405)
<a name="1.4.1"></a>
## <small>1.4.1 (2017-04-25)</small>
* chore: release 1.4.1 ([5ecf0c2](https://github.com/vkarpov15/kareem/commit/5ecf0c2))
* fix: handle numAsyncPres with clone() ([c72e857](https://github.com/vkarpov15/kareem/commit/c72e857)), closes [#8](https://github.com/vkarpov15/kareem/issues/8)
* test: repro #8 ([9b4d6b2](https://github.com/vkarpov15/kareem/commit/9b4d6b2)), closes [#8](https://github.com/vkarpov15/kareem/issues/8)
<a name="1.4.0"></a>
## 1.4.0 (2017-04-19)
* chore: release 1.4.0 ([101c5f5](https://github.com/vkarpov15/kareem/commit/101c5f5))
* feat: add merge() function ([285325e](https://github.com/vkarpov15/kareem/commit/285325e))
<a name="1.3.0"></a>
## 1.3.0 (2017-03-26)
* chore: release 1.3.0 ([f3a9e50](https://github.com/vkarpov15/kareem/commit/f3a9e50))
* feat: pass function args to execPre ([4dd466d](https://github.com/vkarpov15/kareem/commit/4dd466d))
<a name="1.2.1"></a>
## <small>1.2.1 (2017-02-03)</small>
* chore: release 1.2.1 ([d97081f](https://github.com/vkarpov15/kareem/commit/d97081f))
* fix: filter out _kareemIgnored args for error handlers re: Automattic/mongoose#4925 ([ddc7aeb](https://github.com/vkarpov15/kareem/commit/ddc7aeb)), closes [Automattic/mongoose#4925](https://github.com/Automattic/mongoose/issues/4925)
* fix: make error handlers handle errors in pre hooks ([af38033](https://github.com/vkarpov15/kareem/commit/af38033)), closes [Automattic/mongoose#4927](https://github.com/Automattic/mongoose/issues/4927)
<a name="1.2.0"></a>
## 1.2.0 (2017-01-02)
* chore: release 1.2.0 ([033225c](https://github.com/vkarpov15/kareem/commit/033225c))
* chore: upgrade deps ([f9e9a09](https://github.com/vkarpov15/kareem/commit/f9e9a09))
* feat: add _kareemIgnore re: Automattic/mongoose#4836 ([7957771](https://github.com/vkarpov15/kareem/commit/7957771)), closes [Automattic/mongoose#4836](https://github.com/Automattic/mongoose/issues/4836)
<a name="1.1.5"></a>
## <small>1.1.5 (2016-12-13)</small>
* chore: release 1.1.5 ([1a9f684](https://github.com/vkarpov15/kareem/commit/1a9f684))
* fix: correct field name ([04a0e9d](https://github.com/vkarpov15/kareem/commit/04a0e9d))
<a name="1.1.4"></a>
## <small>1.1.4 (2016-12-09)</small>
* chore: release 1.1.4 ([ece401c](https://github.com/vkarpov15/kareem/commit/ece401c))
* chore: run tests on node 6 ([e0cb1cb](https://github.com/vkarpov15/kareem/commit/e0cb1cb))
* fix: only copy own properties in clone() ([dfe28ce](https://github.com/vkarpov15/kareem/commit/dfe28ce)), closes [#7](https://github.com/vkarpov15/kareem/issues/7)
<a name="1.1.3"></a>
## <small>1.1.3 (2016-06-27)</small>
* chore: release 1.1.3 ([87171c8](https://github.com/vkarpov15/kareem/commit/87171c8))
* fix: couple more issues with arg processing ([c65f523](https://github.com/vkarpov15/kareem/commit/c65f523))
<a name="1.1.2"></a>
## <small>1.1.2 (2016-06-27)</small>
* chore: release 1.1.2 ([8e102b6](https://github.com/vkarpov15/kareem/commit/8e102b6))
* fix: add early return ([4feda4e](https://github.com/vkarpov15/kareem/commit/4feda4e))
<a name="1.1.1"></a>
## <small>1.1.1 (2016-06-27)</small>
* chore: release 1.1.1 ([8bb3050](https://github.com/vkarpov15/kareem/commit/8bb3050))
* fix: skip error handlers if no error ([0eb3a44](https://github.com/vkarpov15/kareem/commit/0eb3a44))
<a name="1.1.0"></a>
## 1.1.0 (2016-05-11)
* chore: release 1.1.0 ([85332d9](https://github.com/vkarpov15/kareem/commit/85332d9))
* chore: test on node 4 and node 5 ([1faefa1](https://github.com/vkarpov15/kareem/commit/1faefa1))
* 100% coverage again ([c9aee4e](https://github.com/vkarpov15/kareem/commit/c9aee4e))
* add support for error post hooks ([d378113](https://github.com/vkarpov15/kareem/commit/d378113))
* basic setup for sync hooks #4 ([55aa081](https://github.com/vkarpov15/kareem/commit/55aa081)), closes [#4](https://github.com/vkarpov15/kareem/issues/4)
* proof of concept for error handlers ([e4a07d9](https://github.com/vkarpov15/kareem/commit/e4a07d9))
* refactor out handleWrapError helper ([b19af38](https://github.com/vkarpov15/kareem/commit/b19af38))
<a name="1.0.1"></a>
## <small>1.0.1 (2015-05-10)</small>
* Fix #1 ([de60dc6](https://github.com/vkarpov15/kareem/commit/de60dc6)), closes [#1](https://github.com/vkarpov15/kareem/issues/1)
* release 1.0.1 ([6971088](https://github.com/vkarpov15/kareem/commit/6971088))
* Run tests on iojs in travis ([adcd201](https://github.com/vkarpov15/kareem/commit/adcd201))
* support legacy post hook behavior in wrap() ([23fa74c](https://github.com/vkarpov15/kareem/commit/23fa74c))
* Use node 0.12 in travis ([834689d](https://github.com/vkarpov15/kareem/commit/834689d))
<a name="1.0.0"></a>
## 1.0.0 (2015-01-28)
* Tag 1.0.0 ([4c5a35a](https://github.com/vkarpov15/kareem/commit/4c5a35a))
<a name="0.0.8"></a>
## <small>0.0.8 (2015-01-27)</small>
* Add clone function ([688bba7](https://github.com/vkarpov15/kareem/commit/688bba7))
* Add jscs for style checking ([5c93149](https://github.com/vkarpov15/kareem/commit/5c93149))
* Bump 0.0.8 ([03c0d2f](https://github.com/vkarpov15/kareem/commit/03c0d2f))
* Fix jscs config, add gulp rules ([9989abf](https://github.com/vkarpov15/kareem/commit/9989abf))
* fix Makefile typo ([1f7e61a](https://github.com/vkarpov15/kareem/commit/1f7e61a))
<a name="0.0.7"></a>
## <small>0.0.7 (2015-01-04)</small>
* Bump 0.0.7 ([98ef173](https://github.com/vkarpov15/kareem/commit/98ef173))
* fix LearnBoost/mongoose#2553 - use null instead of undefined for err ([9157b48](https://github.com/vkarpov15/kareem/commit/9157b48)), closes [LearnBoost/mongoose#2553](https://github.com/LearnBoost/mongoose/issues/2553)
* Regenerate docs ([2331cdf](https://github.com/vkarpov15/kareem/commit/2331cdf))
<a name="0.0.6"></a>
## <small>0.0.6 (2015-01-01)</small>
* Update docs and bump 0.0.6 ([92c12a7](https://github.com/vkarpov15/kareem/commit/92c12a7))
<a name="0.0.5"></a>
## <small>0.0.5 (2015-01-01)</small>
* Add coverage rule to Makefile ([825a91c](https://github.com/vkarpov15/kareem/commit/825a91c))
* Add coveralls to README ([fb52369](https://github.com/vkarpov15/kareem/commit/fb52369))
* Add coveralls to travis ([93f6f15](https://github.com/vkarpov15/kareem/commit/93f6f15))
* Add createWrapper() function ([ea77741](https://github.com/vkarpov15/kareem/commit/ea77741))
* Add istanbul code coverage ([6eceeef](https://github.com/vkarpov15/kareem/commit/6eceeef))
* Add some more comments for examples ([c5b0c6f](https://github.com/vkarpov15/kareem/commit/c5b0c6f))
* Add travis ([e6dcb06](https://github.com/vkarpov15/kareem/commit/e6dcb06))
* Add travis badge to docs ([ad8c9b3](https://github.com/vkarpov15/kareem/commit/ad8c9b3))
* Add wrap() tests, 100% coverage ([6945be4](https://github.com/vkarpov15/kareem/commit/6945be4))
* Better test coverage for execPost ([d9ad539](https://github.com/vkarpov15/kareem/commit/d9ad539))
* Bump 0.0.5 ([69875b1](https://github.com/vkarpov15/kareem/commit/69875b1))
* Docs fix ([15b7098](https://github.com/vkarpov15/kareem/commit/15b7098))
* Fix silly mistake in docs generation ([50373eb](https://github.com/vkarpov15/kareem/commit/50373eb))
* Fix typo in readme ([fec4925](https://github.com/vkarpov15/kareem/commit/fec4925))
* Linkify travis badge ([92b25fe](https://github.com/vkarpov15/kareem/commit/92b25fe))
* Make travis run coverage ([747157b](https://github.com/vkarpov15/kareem/commit/747157b))
* Move travis status badge ([d52e89b](https://github.com/vkarpov15/kareem/commit/d52e89b))
* Quick fix for coverage ([50bbddb](https://github.com/vkarpov15/kareem/commit/50bbddb))
* Typo fix ([adea794](https://github.com/vkarpov15/kareem/commit/adea794))
<a name="0.0.4"></a>
## <small>0.0.4 (2014-12-13)</small>
* Bump 0.0.4, run docs generation ([51a15fe](https://github.com/vkarpov15/kareem/commit/51a15fe))
* Use correct post parameters in wrap() ([9bb5da3](https://github.com/vkarpov15/kareem/commit/9bb5da3))
<a name="0.0.3"></a>
## <small>0.0.3 (2014-12-12)</small>
* Add npm test script, fix small bug with args not getting passed through post ([49e3e68](https://github.com/vkarpov15/kareem/commit/49e3e68))
* Bump 0.0.3 ([65621d8](https://github.com/vkarpov15/kareem/commit/65621d8))
* Update readme ([901388b](https://github.com/vkarpov15/kareem/commit/901388b))
<a name="0.0.2"></a>
## <small>0.0.2 (2014-12-12)</small>
* Add github repo and bump 0.0.2 ([59db8be](https://github.com/vkarpov15/kareem/commit/59db8be))
<a name="0.0.1"></a>
## <small>0.0.1 (2014-12-12)</small>
* Add basic docs ([ad29ea4](https://github.com/vkarpov15/kareem/commit/ad29ea4))
* Add pre hooks ([2ffc356](https://github.com/vkarpov15/kareem/commit/2ffc356))
* Add wrap function ([68c540c](https://github.com/vkarpov15/kareem/commit/68c540c))
* Bump to version 0.0.1 ([a4bfd68](https://github.com/vkarpov15/kareem/commit/a4bfd68))
* Initial commit ([4002458](https://github.com/vkarpov15/kareem/commit/4002458))
* Initial deposit ([98fc489](https://github.com/vkarpov15/kareem/commit/98fc489))
* Post hooks ([395b67c](https://github.com/vkarpov15/kareem/commit/395b67c))
* Some basic setup work ([82df75e](https://github.com/vkarpov15/kareem/commit/82df75e))
* Support sync pre hooks ([1cc1b9f](https://github.com/vkarpov15/kareem/commit/1cc1b9f))
* Update package.json description ([978da18](https://github.com/vkarpov15/kareem/commit/978da18))

202
backend/node_modules/kareem/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014-2022 mongoosejs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

385
backend/node_modules/kareem/README.md generated vendored Normal file
View File

@@ -0,0 +1,385 @@
# kareem
[![Build Status](https://github.com/mongoosejs/kareem/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/mongoosejs/kareem/actions/workflows/test.yml)
<!--[![Coverage Status](https://img.shields.io/coveralls/vkarpov15/kareem.svg)](https://coveralls.io/r/vkarpov15/kareem)-->
Re-imagined take on the [hooks](http://npmjs.org/package/hooks) module, meant to offer additional flexibility in allowing you to execute hooks whenever necessary, as opposed to simply wrapping a single function.
Named for the NBA's 2nd all-time leading scorer Kareem Abdul-Jabbar, known for his mastery of the [hook shot](http://en.wikipedia.org/wiki/Kareem_Abdul-Jabbar#Skyhook)
<img src="http://upload.wikimedia.org/wikipedia/commons/0/00/Kareem-Abdul-Jabbar_Lipofsky.jpg" width="220">
<!--DOCS START-->
# API
## pre hooks
Much like [hooks](https://npmjs.org/package/hooks), kareem lets you define
pre and post hooks: pre hooks are called before a given function executes.
Unlike hooks, kareem stores hooks and other internal state in a separate
object, rather than relying on inheritance. Furthermore, kareem exposes
an `execPre()` function that allows you to execute your pre hooks when
appropriate, giving you more fine-grained control over your function hooks.
### It runs without any hooks specified
```javascript
await hooks.execPre('cook', null);
```
### It runs basic serial pre hooks
pre hook functions can return a promise that resolves when finished.
```javascript
let count = 0;
hooks.pre('cook', function() {
++count;
return Promise.resolve();
});
await hooks.execPre('cook', null);
assert.equal(1, count);
```
### It can run multiple pre hooks
```javascript
let count1 = 0;
let count2 = 0;
hooks.pre('cook', function() {
++count1;
return Promise.resolve();
});
hooks.pre('cook', function() {
++count2;
return Promise.resolve();
});
await hooks.execPre('cook', null);
assert.equal(1, count1);
assert.equal(1, count2);
```
### It can run fully synchronous pre hooks
If your pre hook function takes no parameters, its assumed to be
fully synchronous.
```javascript
let count1 = 0;
let count2 = 0;
hooks.pre('cook', function() {
++count1;
});
hooks.pre('cook', function() {
++count2;
});
await hooks.execPre('cook', null);
assert.equal(1, count1);
assert.equal(1, count2);
```
### It properly attaches context to pre hooks
Pre save hook functions are bound to the second parameter to `execPre()`
```javascript
hooks.pre('cook', function() {
this.bacon = 3;
});
hooks.pre('cook', function() {
this.eggs = 4;
});
const obj = { bacon: 0, eggs: 0 };
// In the pre hooks, `this` will refer to `obj`
await hooks.execPre('cook', obj);
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
```
### It supports returning a promise
You can also return a promise from your pre hooks instead of calling
`next()`. When the returned promise resolves, kareem will kick off the
next middleware.
```javascript
hooks.pre('cook', function() {
return new Promise(resolve => {
setTimeout(() => {
this.bacon = 3;
resolve();
}, 100);
});
});
const obj = { bacon: 0 };
await hooks.execPre('cook', obj);
assert.equal(3, obj.bacon);
```
### It supports filtering which hooks to run
You can pass a `filter` option to `execPre()` to select which hooks
to run. The filter function receives each hook object and should return
`true` to run the hook or `false` to skip it.
```javascript
const execed = [];
const fn1 = function() { execed.push('first'); };
fn1.skipMe = true;
hooks.pre('cook', fn1);
const fn2 = function() { execed.push('second'); };
hooks.pre('cook', fn2);
// Only runs fn2, skips fn1 because fn1.skipMe is true
await hooks.execPre('cook', null, [], {
filter: hook => !hook.fn.skipMe
});
assert.deepStrictEqual(execed, ['second']);
```
## post hooks
### It runs without any hooks specified
```javascript
const [eggs] = await hooks.execPost('cook', null, [1]);
assert.equal(eggs, 1);
```
### It executes with parameters passed in
```javascript
hooks.post('cook', function(eggs, bacon, callback) {
assert.equal(eggs, 1);
assert.equal(bacon, 2);
callback();
});
const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
assert.equal(eggs, 1);
assert.equal(bacon, 2);
```
### It can use synchronous post hooks
```javascript
const execed = {};
hooks.post('cook', function(eggs, bacon) {
execed.first = true;
assert.equal(eggs, 1);
assert.equal(bacon, 2);
});
hooks.post('cook', function(eggs, bacon, callback) {
execed.second = true;
assert.equal(eggs, 1);
assert.equal(bacon, 2);
callback();
});
const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]);
assert.equal(Object.keys(execed).length, 2);
assert.ok(execed.first);
assert.ok(execed.second);
assert.equal(eggs, 1);
assert.equal(bacon, 2);
```
### It supports returning a promise
You can also return a promise from your post hooks instead of calling
`next()`. When the returned promise resolves, kareem will kick off the
next middleware.
```javascript
hooks.post('cook', function() {
return new Promise(resolve => {
setTimeout(() => {
this.bacon = 3;
resolve();
}, 100);
});
});
const obj = { bacon: 0 };
await hooks.execPost('cook', obj, [obj]);
assert.equal(obj.bacon, 3);
```
### It supports filtering which hooks to run
You can pass a `filter` option to `execPost()` to select which hooks
to run. The filter function receives each hook object and should return
`true` to run the hook or `false` to skip it.
```javascript
const execed = [];
const fn1 = function() { execed.push('first'); };
fn1.skipMe = true;
hooks.post('cook', fn1);
const fn2 = function() { execed.push('second'); };
hooks.post('cook', fn2);
// Only runs fn2, skips fn1 because fn1.skipMe is true
await hooks.execPost('cook', null, [], {
filter: hook => !hook.fn.skipMe
});
assert.deepStrictEqual(execed, ['second']);
```
## wrap()
### It wraps pre and post calls into one call
```javascript
hooks.pre('cook', function() {
return new Promise(resolve => {
this.bacon = 3;
setTimeout(() => {
resolve();
}, 5);
});
});
hooks.pre('cook', function() {
this.eggs = 4;
return Promise.resolve();
});
hooks.pre('cook', function() {
this.waffles = false;
return Promise.resolve();
});
hooks.post('cook', function(obj) {
obj.tofu = 'no';
});
const obj = { bacon: 0, eggs: 0 };
const args = [obj];
const result = await hooks.wrap(
'cook',
function(o) {
assert.equal(obj.bacon, 3);
assert.equal(obj.eggs, 4);
assert.equal(obj.waffles, false);
assert.equal(obj.tofu, undefined);
return o;
},
obj,
args);
assert.equal(obj.bacon, 3);
assert.equal(obj.eggs, 4);
assert.equal(obj.waffles, false);
assert.equal(obj.tofu, 'no');
assert.equal(result, obj);
```
## createWrapper()
### It wraps wrap() into a callable function
```javascript
hooks.pre('cook', function() {
this.bacon = 3;
return Promise.resolve();
});
hooks.pre('cook', function() {
return new Promise(resolve => {
this.eggs = 4;
setTimeout(function() {
resolve();
}, 10);
});
});
hooks.pre('cook', function() {
this.waffles = false;
return Promise.resolve();
});
hooks.post('cook', function(obj) {
obj.tofu = 'no';
});
const obj = { bacon: 0, eggs: 0 };
const cook = hooks.createWrapper(
'cook',
function(o) {
assert.equal(3, obj.bacon);
assert.equal(4, obj.eggs);
assert.equal(false, obj.waffles);
assert.equal(undefined, obj.tofu);
return o;
},
obj);
const result = await cook(obj);
assert.equal(obj.bacon, 3);
assert.equal(obj.eggs, 4);
assert.equal(obj.waffles, false);
assert.equal(obj.tofu, 'no');
assert.equal(result, obj);
```
## clone()
### It clones a Kareem object
```javascript
const k1 = new Kareem();
k1.pre('cook', function() {});
k1.post('cook', function() {});
const k2 = k1.clone();
assert.deepEqual(Array.from(k2._pres.keys()), ['cook']);
assert.deepEqual(Array.from(k2._posts.keys()), ['cook']);
```
## merge()
### It pulls hooks from another Kareem object
```javascript
const k1 = new Kareem();
const test1 = function() {};
k1.pre('cook', test1);
k1.post('cook', function() {});
const k2 = new Kareem();
const test2 = function() {};
k2.pre('cook', test2);
const k3 = k2.merge(k1);
assert.equal(k3._pres.get('cook').length, 2);
assert.equal(k3._pres.get('cook')[0].fn, test2);
assert.equal(k3._pres.get('cook')[1].fn, test1);
assert.equal(k3._posts.get('cook').length, 1);
```

5
backend/node_modules/kareem/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,5 @@
## Security contact information
To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

View File

@@ -0,0 +1,224 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}
table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }
.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* yellow */
.cbranch-no { background: yellow !important; color: #111; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
.highlighted,
.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
background: #C21F39 !important;
}
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
.medium .chart { border:1px solid #f9cd0b; }
/* light yellow */
.medium { background: #fff4c2; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
span.cline-neutral { background: #eaeaea; }
.coverage-summary td.empty {
opacity: .5;
padding-top: 4px;
padding-bottom: 4px;
line-height: 1;
color: #888;
}
.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}

View File

@@ -0,0 +1,87 @@
/* eslint-disable */
var jumpToCode = (function init() {
// Classes of code we would like to highlight in the file view
var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
// Elements to highlight in the file listing view
var fileListingElements = ['td.pct.low'];
// We don't want to select elements that are direct descendants of another match
var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
// Selecter that finds elements on the page to which we can jump
var selector =
fileListingElements.join(', ') +
', ' +
notSelector +
missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
// The NodeList of matching elements
var missingCoverageElements = document.querySelectorAll(selector);
var currentIndex;
function toggleClass(index) {
missingCoverageElements
.item(currentIndex)
.classList.remove('highlighted');
missingCoverageElements.item(index).classList.add('highlighted');
}
function makeCurrent(index) {
toggleClass(index);
currentIndex = index;
missingCoverageElements.item(index).scrollIntoView({
behavior: 'smooth',
block: 'center',
inline: 'center'
});
}
function goToPrevious() {
var nextIndex = 0;
if (typeof currentIndex !== 'number' || currentIndex === 0) {
nextIndex = missingCoverageElements.length - 1;
} else if (missingCoverageElements.length > 1) {
nextIndex = currentIndex - 1;
}
makeCurrent(nextIndex);
}
function goToNext() {
var nextIndex = 0;
if (
typeof currentIndex === 'number' &&
currentIndex < missingCoverageElements.length - 1
) {
nextIndex = currentIndex + 1;
}
makeCurrent(nextIndex);
}
return function jump(event) {
if (
document.getElementById('fileSearch') === document.activeElement &&
document.activeElement != null
) {
// if we're currently focused on the search input, we don't want to navigate
return;
}
switch (event.which) {
case 78: // n
case 74: // j
goToNext();
break;
case 66: // b
case 75: // k
case 80: // p
goToPrevious();
break;
}
};
})();
window.addEventListener('keydown', jumpToCode);

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>All files</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">96.66% </span>
<span class="quiet">Statements</span>
<span class='fraction'>232/240</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">90.08% </span>
<span class="quiet">Branches</span>
<span class='fraction'>109/121</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>30/30</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">96.56% </span>
<span class="quiet">Lines</span>
<span class='fraction'>225/233</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="index.js"><a href="index.js.html">index.js</a></td>
<td data-value="96.66" class="pic high">
<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
</td>
<td data-value="96.66" class="pct high">96.66%</td>
<td data-value="240" class="abs high">232/240</td>
<td data-value="90.08" class="pct high">90.08%</td>
<td data-value="121" class="abs high">109/121</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="30" class="abs high">30/30</td>
<td data-value="96.56" class="pct high">96.56%</td>
<td data-value="233" class="abs high">225/233</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-08-25T20:16:45.952Z
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

View File

@@ -0,0 +1,196 @@
/* eslint-disable */
var addSorting = (function() {
'use strict';
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() {
return document.querySelector('.coverage-summary');
}
// returns the thead element of the summary table
function getTableHeader() {
return getTable().querySelector('thead tr');
}
// returns the tbody element of the summary table
function getTableBody() {
return getTable().querySelector('tbody');
}
// returns the th element for nth column
function getNthColumn(n) {
return getTableHeader().querySelectorAll('th')[n];
}
function onFilterInput() {
const searchValue = document.getElementById('fileSearch').value;
const rows = document.getElementsByTagName('tbody')[0].children;
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
if (
row.textContent
.toLowerCase()
.includes(searchValue.toLowerCase())
) {
row.style.display = '';
} else {
row.style.display = 'none';
}
}
}
// loads the search box
function addSearchBox() {
var template = document.getElementById('filterTemplate');
var templateClone = template.content.cloneNode(true);
templateClone.getElementById('fileSearch').oninput = onFilterInput;
template.parentElement.appendChild(templateClone);
}
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML =
colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function(a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function(a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc
? ' sorted-desc'
: ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function() {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i = 0; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function() {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData();
addSearchBox();
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

423
backend/node_modules/kareem/coverage/lcov.info generated vendored Normal file
View File

@@ -0,0 +1,423 @@
TN:
SF:index.js
FN:6,Kareem
FN:11,skipWrappedFunction
FN:19,overwriteResult
FN:34,execPre
FN:49,(anonymous_4)
FN:53,(anonymous_5)
FN:103,(anonymous_6)
FN:120,execPost
FN:157,(anonymous_8)
FN:161,nextCallback
FN:234,(anonymous_10)
FN:254,(anonymous_11)
FN:256,syncWrapper
FN:276,wrap
FN:304,(anonymous_14)
FN:310,(anonymous_15)
FN:324,(anonymous_16)
FN:343,(anonymous_17)
FN:355,(anonymous_18)
FN:361,kareemWrappedFunction
FN:376,(anonymous_20)
FN:409,(anonymous_21)
FN:440,postError
FN:453,(anonymous_23)
FN:473,(anonymous_24)
FN:481,(anonymous_25)
FN:481,(anonymous_26)
FN:488,(anonymous_27)
FN:495,isPromiseLike
FN:499,isErrorHandlingMiddleware
FNF:30
FNH:30
FNDA:62,Kareem
FNDA:6,skipWrappedFunction
FNDA:12,overwriteResult
FNDA:27,execPre
FNDA:27,(anonymous_4)
FNDA:27,(anonymous_5)
FNDA:4,(anonymous_6)
FNDA:27,execPost
FNDA:41,(anonymous_8)
FNDA:24,nextCallback
FNDA:5,(anonymous_10)
FNDA:2,(anonymous_11)
FNDA:2,syncWrapper
FNDA:17,wrap
FNDA:2,(anonymous_14)
FNDA:6,(anonymous_15)
FNDA:6,(anonymous_16)
FNDA:2,(anonymous_17)
FNDA:1,(anonymous_18)
FNDA:1,kareemWrappedFunction
FNDA:51,(anonymous_20)
FNDA:60,(anonymous_21)
FNDA:1,postError
FNDA:4,(anonymous_23)
FNDA:1,(anonymous_24)
FNDA:1,(anonymous_25)
FNDA:1,(anonymous_26)
FNDA:1,(anonymous_27)
FNDA:61,isPromiseLike
FNDA:41,isErrorHandlingMiddleware
DA:7,62
DA:8,62
DA:11,1
DA:12,6
DA:13,3
DA:16,3
DA:19,1
DA:20,12
DA:21,6
DA:24,6
DA:34,1
DA:35,27
DA:36,27
DA:37,27
DA:38,27
DA:40,27
DA:41,3
DA:44,24
DA:45,24
DA:46,33
DA:47,27
DA:48,27
DA:49,27
DA:50,27
DA:51,27
DA:53,27
DA:54,27
DA:55,27
DA:56,19
DA:57,0
DA:59,19
DA:62,27
DA:63,27
DA:64,27
DA:65,0
DA:67,27
DA:70,9
DA:71,3
DA:72,3
DA:74,6
DA:77,6
DA:78,6
DA:80,2
DA:81,0
DA:82,0
DA:84,2
DA:89,16
DA:91,16
DA:92,3
DA:103,1
DA:104,4
DA:105,4
DA:107,4
DA:108,4
DA:120,1
DA:121,27
DA:122,27
DA:124,27
DA:125,27
DA:126,6
DA:129,27
DA:130,1
DA:131,0
DA:133,1
DA:136,26
DA:137,41
DA:138,41
DA:139,41
DA:140,41
DA:141,41
DA:142,40
DA:143,40
DA:144,40
DA:148,41
DA:149,1
DA:150,1
DA:151,1
DA:157,41
DA:158,41
DA:159,41
DA:161,41
DA:162,24
DA:163,12
DA:165,12
DA:169,41
DA:170,14
DA:171,8
DA:172,8
DA:173,8
DA:174,1
DA:175,7
DA:177,6
DA:180,5
DA:181,0
DA:182,0
DA:184,5
DA:187,6
DA:190,27
DA:192,1
DA:194,26
DA:195,26
DA:196,26
DA:197,26
DA:198,3
DA:199,23
DA:201,18
DA:204,7
DA:205,2
DA:206,2
DA:208,5
DA:209,5
DA:212,19
DA:213,2
DA:214,2
DA:220,26
DA:221,11
DA:224,15
DA:234,1
DA:235,5
DA:236,5
DA:238,5
DA:239,6
DA:240,6
DA:241,2
DA:245,5
DA:254,1
DA:255,2
DA:256,2
DA:257,2
DA:259,2
DA:261,2
DA:263,2
DA:276,1
DA:278,17
DA:279,17
DA:280,17
DA:282,7
DA:283,2
DA:284,2
DA:286,5
DA:290,12
DA:291,10
DA:294,9
DA:296,7
DA:304,1
DA:305,2
DA:307,2
DA:308,2
DA:309,4
DA:310,6
DA:313,4
DA:314,1
DA:315,1
DA:318,3
DA:321,2
DA:322,2
DA:323,4
DA:324,6
DA:327,4
DA:328,1
DA:329,1
DA:332,3
DA:335,2
DA:343,1
DA:344,2
DA:355,1
DA:356,1
DA:357,1
DA:359,0
DA:361,1
DA:362,1
DA:363,1
DA:376,1
DA:377,51
DA:378,43
DA:379,43
DA:380,8
DA:381,1
DA:384,51
DA:385,51
DA:387,51
DA:388,1
DA:391,50
DA:392,2
DA:394,48
DA:397,50
DA:409,1
DA:410,60
DA:412,60
DA:413,52
DA:414,52
DA:415,52
DA:418,60
DA:419,1
DA:422,59
DA:423,2
DA:425,57
DA:427,59
DA:428,59
DA:440,1
DA:441,1
DA:442,1
DA:443,1
DA:444,1
DA:446,1
DA:453,1
DA:454,4
DA:456,4
DA:457,6
DA:458,6
DA:460,4
DA:461,5
DA:464,4
DA:473,1
DA:474,1
DA:475,1
DA:477,1
DA:478,1
DA:479,1
DA:481,1
DA:482,1
DA:483,1
DA:485,1
DA:486,1
DA:487,1
DA:488,1
DA:489,1
DA:492,1
DA:496,61
DA:500,41
DA:501,2
DA:503,39
DA:506,1
LF:233
LH:225
BRDA:12,0,0,3
BRDA:12,0,1,3
BRDA:20,1,0,6
BRDA:20,1,1,6
BRDA:35,2,0,27
BRDA:35,2,1,3
BRDA:40,3,0,3
BRDA:40,3,1,24
BRDA:46,4,0,27
BRDA:46,4,1,6
BRDA:53,5,0,9
BRDA:53,5,1,18
BRDA:56,6,0,0
BRDA:56,6,1,19
BRDA:56,7,0,19
BRDA:56,7,1,19
BRDA:64,8,0,0
BRDA:64,8,1,27
BRDA:70,9,0,3
BRDA:70,9,1,6
BRDA:80,10,0,0
BRDA:80,10,1,2
BRDA:91,11,0,3
BRDA:91,11,1,13
BRDA:104,12,0,4
BRDA:104,12,1,1
BRDA:108,13,0,4
BRDA:108,13,1,2
BRDA:121,14,0,27
BRDA:121,14,1,1
BRDA:125,15,0,6
BRDA:125,15,1,21
BRDA:125,16,0,27
BRDA:125,16,1,10
BRDA:129,17,0,1
BRDA:129,17,1,26
BRDA:130,18,0,0
BRDA:130,18,1,1
BRDA:142,19,0,40
BRDA:142,19,1,0
BRDA:142,20,0,40
BRDA:142,20,1,37
BRDA:148,21,0,1
BRDA:148,21,1,40
BRDA:162,22,0,12
BRDA:162,22,1,12
BRDA:169,23,0,14
BRDA:169,23,1,27
BRDA:170,24,0,8
BRDA:170,24,1,6
BRDA:173,25,0,1
BRDA:173,25,1,7
BRDA:175,26,0,6
BRDA:175,26,1,1
BRDA:180,27,0,0
BRDA:180,27,1,5
BRDA:190,28,0,1
BRDA:190,28,1,26
BRDA:197,29,0,3
BRDA:197,29,1,23
BRDA:199,30,0,18
BRDA:199,30,1,5
BRDA:204,31,0,2
BRDA:204,31,1,5
BRDA:212,32,0,2
BRDA:212,32,1,17
BRDA:220,33,0,11
BRDA:220,33,1,15
BRDA:235,34,0,5
BRDA:235,34,1,1
BRDA:239,35,0,6
BRDA:239,35,1,2
BRDA:240,36,0,2
BRDA:240,36,1,4
BRDA:282,37,0,2
BRDA:282,37,1,5
BRDA:290,38,0,10
BRDA:290,38,1,2
BRDA:313,39,0,1
BRDA:313,39,1,3
BRDA:327,40,0,1
BRDA:327,40,1,3
BRDA:344,41,0,2
BRDA:344,41,1,1
BRDA:357,42,0,0
BRDA:357,42,1,1
BRDA:362,43,0,1
BRDA:362,43,1,0
BRDA:377,44,0,43
BRDA:377,44,1,8
BRDA:380,45,0,1
BRDA:380,45,1,7
BRDA:384,46,0,51
BRDA:384,46,1,35
BRDA:387,47,0,1
BRDA:387,47,1,50
BRDA:391,48,0,2
BRDA:391,48,1,48
BRDA:410,49,0,60
BRDA:410,49,1,40
BRDA:412,50,0,52
BRDA:412,50,1,8
BRDA:418,51,0,1
BRDA:418,51,1,59
BRDA:422,52,0,2
BRDA:422,52,1,57
BRDA:441,53,0,1
BRDA:441,53,1,0
BRDA:474,54,0,1
BRDA:474,54,1,0
BRDA:475,55,0,1
BRDA:475,55,1,0
BRDA:478,56,0,1
BRDA:478,56,1,0
BRDA:486,57,0,1
BRDA:486,57,1,1
BRDA:496,58,0,61
BRDA:496,58,1,5
BRDA:496,58,2,5
BRDA:500,59,0,2
BRDA:500,59,1,39
BRF:121
BRH:109
end_of_record

31
backend/node_modules/kareem/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,31 @@
declare module "kareem" {
export default class Kareem {
static skipWrappedFunction(): SkipWrappedFunction;
static overwriteMiddlewareResult(): OverwriteMiddlewareResult;
static overwriteArguments(): OverwriteArguments;
pre(name: string | RegExp, fn: Function): this;
pre(name: string | RegExp, options: Record<string, any>, fn: Function, error?: any, unshift?: boolean): this;
post(name: string | RegExp, fn: Function): this;
post(name: string | RegExp, options: Record<string, any>, fn: Function, unshift?: boolean): this;
clone(): Kareem;
merge(other: Kareem, clone?: boolean): this;
createWrapper(name: string, fn: Function, context?: any, options?: Record<string, any>): Function;
createWrapperSync(name: string, fn: Function): Function;
hasHooks(name: string): boolean;
filter(fn: Function): Kareem;
wrap(name: string, fn: Function, context: any, args: any[], options?: Record<string, any>): Function;
execPostSync(name: string, context: any, args: any[]): any;
execPost(name: string, context: any, args: any[], options?: Record<string, any>, callback?: Function): void;
execPreSync(name: string, context: any, args: any[]): any;
execPre(name: string, context: any, args: any[], callback?: Function): void;
}
class SkipWrappedFunction {}
class OverwriteMiddlewareResult {}
class OverwriteArguments {}
}

539
backend/node_modules/kareem/index.js generated vendored Normal file
View File

@@ -0,0 +1,539 @@
'use strict';
/**
* Create a new instance
*/
function Kareem() {
this._pres = new Map();
this._posts = new Map();
}
Kareem.skipWrappedFunction = function skipWrappedFunction() {
if (!(this instanceof Kareem.skipWrappedFunction)) {
return new Kareem.skipWrappedFunction(...arguments);
}
this.args = [...arguments];
};
Kareem.overwriteResult = function overwriteResult() {
if (!(this instanceof Kareem.overwriteResult)) {
return new Kareem.overwriteResult(...arguments);
}
this.args = [...arguments];
};
Kareem.overwriteArguments = function overwriteArguments() {
if (!(this instanceof Kareem.overwriteArguments)) {
return new Kareem.overwriteArguments(...arguments);
}
this.args = [...arguments];
};
/**
* Execute all "pre" hooks for "name"
* @param {String} name The hook name to execute
* @param {*} context Overwrite the "this" for the hook
* @param {Array} args arguments passed to the pre hooks
* @param {Object} [options] Optional options
* @param {Function} [options.filter] Filter function to select which hooks to run
* @returns {Array} The potentially modified arguments
*/
Kareem.prototype.execPre = async function execPre(name, context, args, options) {
let pres = this._pres.get(name) || [];
if (options?.filter) {
pres = pres.filter(options.filter);
}
const numPres = pres.length;
let $args = args;
let skipWrappedFunction = null;
if (!numPres) {
return $args;
}
for (const pre of pres) {
const args = [];
const _args = [null].concat($args);
for (let i = 1; i < _args.length; ++i) {
if (i === _args.length - 1 && typeof _args[i] === 'function') {
continue; // skip callbacks to avoid accidentally calling the callback from a hook
}
args.push(_args[i]);
}
try {
const maybePromiseLike = pre.fn.apply(context, args);
if (isPromiseLike(maybePromiseLike)) {
const result = await maybePromiseLike;
if (result instanceof Kareem.overwriteArguments) {
$args = result.args;
}
} else if (maybePromiseLike instanceof Kareem.overwriteArguments) {
$args = maybePromiseLike.args;
}
} catch (error) {
if (error instanceof Kareem.skipWrappedFunction) {
skipWrappedFunction = error;
continue;
}
if (error instanceof Kareem.overwriteArguments) {
$args = error.args;
continue;
}
throw error;
}
}
if (skipWrappedFunction) {
throw skipWrappedFunction;
}
return $args;
};
/**
* Execute all "pre" hooks for "name" synchronously
* @param {String} name The hook name to execute
* @param {*} context Overwrite the "this" for the hook
* @param {Array} [args] Apply custom arguments to the hook
* @param {Object} [options] Optional options
* @param {Function} [options.filter] Filter function to select which hooks to run
* @returns {Array} The potentially modified arguments
*/
Kareem.prototype.execPreSync = function(name, context, args, options) {
let pres = this._pres.get(name) || [];
if (options?.filter) {
pres = pres.filter(options.filter);
}
const numPres = pres.length;
let $args = args || [];
for (let i = 0; i < numPres; ++i) {
const result = pres[i].fn.apply(context, $args);
if (result instanceof Kareem.overwriteArguments) {
$args = result.args;
}
}
return $args;
};
/**
* Execute all "post" hooks for "name"
* @param {String} name The hook name to execute
* @param {*} context Overwrite the "this" for the hook
* @param {Array} args Apply custom arguments to the hook
* @param {Object} [options] Optional options
* @param {Error} [options.error] Error to pass to error-handling middleware
* @param {Function} [options.filter] Filter function to select which hooks to run
* @returns {void}
*/
Kareem.prototype.execPost = async function execPost(name, context, args, options) {
let posts = this._posts.get(name) || [];
if (options?.filter) {
posts = posts.filter(options.filter);
}
const numPosts = posts.length;
let firstError = null;
if (options && options.error) {
firstError = options.error;
}
if (!numPosts) {
if (firstError != null) {
throw firstError;
}
return args;
}
for (const currentPost of posts) {
const post = currentPost.fn;
let numArgs = 0;
const newArgs = [];
const argLength = args.length;
for (let i = 0; i < argLength; ++i) {
if (!args[i] || !args[i]._kareemIgnore) {
numArgs += 1;
newArgs.push(args[i]);
}
}
// If numCallbackParams set, fill in the rest with null to enforce consistent number of args
if (options?.numCallbackParams != null) {
numArgs = options.numCallbackParams;
for (let i = newArgs.length; i < numArgs; ++i) {
newArgs.push(null);
}
}
let resolve;
let reject;
const cbPromise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
newArgs.push(function nextCallback(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
if (firstError) {
if (isErrorHandlingMiddleware(currentPost, numArgs)) {
try {
const res = post.apply(context, [firstError].concat(newArgs));
if (isPromiseLike(res)) {
await res;
} else if (post.length === numArgs + 2) {
// `numArgs + 2` because we added the error and the callback
await cbPromise;
}
} catch (error) {
if (error instanceof Kareem.overwriteResult) {
args = error.args;
continue;
}
firstError = error;
}
} else {
continue;
}
} else {
if (isErrorHandlingMiddleware(currentPost, numArgs)) {
// Skip error handlers if no error
continue;
} else {
let res = null;
try {
res = post.apply(context, newArgs);
if (isPromiseLike(res)) {
res = await res;
} else if (post.length === numArgs + 1) {
// If post function takes a callback, wait for the post function to call the callback
res = await cbPromise;
}
} catch (error) {
if (error instanceof Kareem.overwriteResult) {
args = error.args;
continue;
}
firstError = error;
continue;
}
if (res instanceof Kareem.overwriteResult) {
args = res.args;
continue;
}
}
}
}
if (firstError != null) {
throw firstError;
}
return args;
};
/**
* Execute all "post" hooks for "name" synchronously
* @param {String} name The hook name to execute
* @param {*} context Overwrite the "this" for the hook
* @param {Array} args Apply custom arguments to the hook
* @param {Object} [options] Optional options
* @param {Function} [options.filter] Filter function to select which hooks to run
* @returns {Array} The used arguments
*/
Kareem.prototype.execPostSync = function(name, context, args, options) {
let posts = this._posts.get(name) || [];
if (options?.filter) {
posts = posts.filter(options.filter);
}
const numPosts = posts.length;
for (let i = 0; i < numPosts; ++i) {
const res = posts[i].fn.apply(context, args || []);
if (res instanceof Kareem.overwriteResult) {
args = res.args;
}
}
return args;
};
/**
* Create a synchronous wrapper for "fn"
* @param {String} name The name of the hook
* @param {Function} fn The function to wrap
* @param {*} context Overwrite the "this" for the hook. If null/undefined, uses the calling context.
* @param {Object} [options] Options for the wrapper
* @param {Function} [options.getOptions] Function that receives the wrapper arguments and returns options for execPreSync/execPostSync. Can return `{ filter }` for both, or `{ pre: { filter }, post: { filter } }` for separate options.
* @returns {Function} The wrapped function
*/
Kareem.prototype.createWrapperSync = function(name, fn, context, options) {
const _this = this;
const getOptions = options?.getOptions;
return function syncWrapper() {
const _context = context || this;
const args = Array.from(arguments);
const execOptions = typeof getOptions === 'function' ? getOptions(args) : {};
const preOptions = execOptions.pre ?? execOptions;
const postOptions = execOptions.post ?? execOptions;
const modifiedArgs = _this.execPreSync(name, _context, args, preOptions);
const toReturn = fn.apply(_context, modifiedArgs);
const result = _this.execPostSync(name, _context, [toReturn], postOptions);
return result[0];
};
};
/**
* Executes pre hooks, followed by the wrapped function, followed by post hooks.
* @param {String} name The name of the hook
* @param {Function} fn The function for the hook
* @param {*} context Overwrite the "this" for the hook
* @param {Array} args Apply custom arguments to the hook
* @param {Object} options Additional options for the hook
* @returns {void}
*/
Kareem.prototype.wrap = async function wrap(name, fn, context, args, options) {
let ret;
let skipWrappedFunction = false;
let modifiedArgs = args;
try {
modifiedArgs = await this.execPre(name, context, args);
} catch (error) {
if (error instanceof Kareem.skipWrappedFunction) {
ret = error.args;
skipWrappedFunction = true;
} else {
await this.execPost(name, context, args, { ...options, error });
}
}
if (!skipWrappedFunction) {
ret = await fn.apply(context, modifiedArgs);
}
ret = await this.execPost(name, context, [ret], options);
return ret[0];
};
/**
* Filter current instance for something specific and return the filtered clone
* @param {Function} fn The filter function
* @returns {Kareem} The cloned and filtered instance
*/
Kareem.prototype.filter = function(fn) {
const clone = this.clone();
const pres = Array.from(clone._pres.keys());
for (const name of pres) {
const hooks = this._pres.get(name).
map(h => Object.assign({}, h, { name: name })).
filter(fn);
if (hooks.length === 0) {
clone._pres.delete(name);
continue;
}
clone._pres.set(name, hooks);
}
const posts = Array.from(clone._posts.keys());
for (const name of posts) {
const hooks = this._posts.get(name).
map(h => Object.assign({}, h, { name: name })).
filter(fn);
if (hooks.length === 0) {
clone._posts.delete(name);
continue;
}
clone._posts.set(name, hooks);
}
return clone;
};
/**
* Check for a "name" to exist either in pre or post hooks
* @param {String} name The name of the hook
* @returns {Boolean} "true" if found, "false" otherwise
*/
Kareem.prototype.hasHooks = function(name) {
return this._pres.has(name) || this._posts.has(name);
};
/**
* Create a Wrapper for "fn" on "name" and return the wrapped function
* @param {String} name The name of the hook
* @param {Function} fn The function to wrap
* @param {*} context Overwrite the "this" for the hook
* @param {Object} [options]
* @returns {Function} The wrapped function
*/
Kareem.prototype.createWrapper = function(name, fn, context, options) {
const _this = this;
if (!this.hasHooks(name)) {
// Fast path: if there's no hooks for this function, just return the function
return fn;
}
return function kareemWrappedFunction() {
const _context = context || this;
return _this.wrap(name, fn, _context, Array.from(arguments), options);
};
};
/**
* Register a new hook for "pre"
* @param {String} name The name of the hook
* @param {Object} [options]
* @param {Function} fn The function to register for "name"
* @param {never} error Unused
* @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
* @returns {Kareem}
*/
Kareem.prototype.pre = function(name, options, fn, error, unshift) {
if (typeof options === 'function') {
fn = options;
options = {};
} else if (options == null) {
options = {};
}
const pres = this._pres.get(name) || [];
this._pres.set(name, pres);
if (typeof fn !== 'function') {
throw new Error('pre() requires a function, got "' + typeof fn + '"');
}
if (unshift) {
pres.unshift(Object.assign({}, options, { fn: fn }));
} else {
pres.push(Object.assign({}, options, { fn: fn }));
}
return this;
};
/**
* Register a new hook for "post"
* @param {String} name The name of the hook
* @param {Object} [options]
* @param {Boolean} [options.errorHandler] Whether this is an error handler
* @param {Function} fn The function to register for "name"
* @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
* @returns {Kareem}
*/
Kareem.prototype.post = function(name, options, fn, unshift) {
const posts = this._posts.get(name) || [];
if (typeof options === 'function') {
unshift = !!fn;
fn = options;
options = {};
}
if (typeof fn !== 'function') {
throw new Error('post() requires a function, got "' + typeof fn + '"');
}
if (unshift) {
posts.unshift(Object.assign({}, options, { fn: fn }));
} else {
posts.push(Object.assign({}, options, { fn: fn }));
}
this._posts.set(name, posts);
return this;
};
/**
* Register a new error handler for "name"
* @param {String} name The name of the hook
* @param {Object} [options]
* @param {Function} fn The function to register for "name"
* @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
* @returns {Kareem}
*/
Kareem.prototype.postError = function postError(name, options, fn, unshift) {
if (typeof options === 'function') {
unshift = !!fn;
fn = options;
options = {};
}
return this.post(name, { ...options, errorHandler: true }, fn, unshift);
};
/**
* Clone the current instance
* @returns {Kareem} The cloned instance
*/
Kareem.prototype.clone = function() {
const n = new Kareem();
for (const key of this._pres.keys()) {
const clone = this._pres.get(key).slice();
n._pres.set(key, clone);
}
for (const key of this._posts.keys()) {
n._posts.set(key, this._posts.get(key).slice());
}
return n;
};
/**
* Merge "other" into self or "clone"
* @param {Kareem} other The instance to merge with
* @param {Kareem} [clone] The instance to merge onto (if not defined, using "this")
* @returns {Kareem} The merged instance
*/
Kareem.prototype.merge = function(other, clone) {
clone = arguments.length === 1 ? true : clone;
const ret = clone ? this.clone() : this;
for (const key of other._pres.keys()) {
const sourcePres = ret._pres.get(key) || [];
const deduplicated = other._pres.get(key).
// Deduplicate based on `fn`
filter(p => sourcePres.map(_p => _p.fn).indexOf(p.fn) === -1);
const combined = sourcePres.concat(deduplicated);
ret._pres.set(key, combined);
}
for (const key of other._posts.keys()) {
const sourcePosts = ret._posts.get(key) || [];
const deduplicated = other._posts.get(key).
filter(p => sourcePosts.indexOf(p) === -1);
ret._posts.set(key, sourcePosts.concat(deduplicated));
}
return ret;
};
function isPromiseLike(v) {
return (typeof v === 'object' && v !== null && typeof v.then === 'function');
}
function isErrorHandlingMiddleware(post, numArgs) {
if (post.errorHandler) {
return true;
}
return post.fn.length === numArgs + 2;
}
module.exports = Kareem;

28
backend/node_modules/kareem/package.json generated vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "kareem",
"version": "3.2.0",
"description": "Next-generation take on pre/post function hooks",
"main": "index.js",
"scripts": {
"lint": "eslint .",
"test": "mocha ./test/*",
"test-coverage": "nyc --reporter lcov mocha ./test/*",
"docs": "node ./docs.js"
},
"repository": {
"type": "git",
"url": "git://github.com/mongoosejs/kareem.git"
},
"devDependencies": {
"acquit": "1.x",
"acquit-ignore": "0.2.x",
"eslint": "8.20.0",
"mocha": "11.x",
"nyc": "15.1.0"
},
"author": "Valeri Karpov <val@karpov.io>",
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
}
}

22
backend/node_modules/mongoose/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# MIT License
Copyright (c) 2010-2013 LearnBoost <dev@learnboost.com>
Copyright (c) 2013-2021 Automattic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

395
backend/node_modules/mongoose/README.md generated vendored Normal file
View File

@@ -0,0 +1,395 @@
# Mongoose
Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment. Mongoose supports [Node.js](https://nodejs.org/en/) and [Deno](https://deno.land/) (alpha).
[![Build Status](https://github.com/Automattic/mongoose/workflows/Test/badge.svg)](https://github.com/Automattic/mongoose)
[![NPM version](https://badge.fury.io/js/mongoose.svg)](http://badge.fury.io/js/mongoose)
[![Deno version](https://deno.land/badge/mongoose/version)](https://deno.land/x/mongoose)
[![Deno popularity](https://deno.land/badge/mongoose/popularity)](https://deno.land/x/mongoose)
[![npm](https://nodei.co/npm/mongoose.png)](https://www.npmjs.com/package/mongoose)
## Documentation
The official documentation website is [mongoosejs.com](https://mongoosejs.com/).
Mongoose 9.0.0 was released on November 21, 2025. You can find more details on [backwards breaking changes in 9.0.0 on our docs site](https://mongoosejs.com/docs/migrating_to_9.html).
## Support
* [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoose)
* [Bug Reports](https://github.com/Automattic/mongoose/issues/)
* [Mongoose Slack Channel](http://slack.mongoosejs.io/)
* [Help Forum](http://groups.google.com/group/mongoose-orm)
* [MongoDB Support](https://www.mongodb.com/docs/manual/support/)
## Plugins
Check out the [plugins search site](https://plugins.mongoosejs.io/) to see hundreds of related modules from the community. Next, learn how to write your own plugin from the [docs](https://mongoosejs.com/docs/plugins.html) or [this blog post](http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins).
## Contributors
Pull requests are always welcome! Please base pull requests against the `master`
branch and follow the [contributing guide](https://github.com/Automattic/mongoose/blob/master/CONTRIBUTING.md).
If your pull requests makes documentation changes, please do **not**
modify any `.html` files. The `.html` files are compiled code, so please make
your changes in `docs/*.pug`, `lib/*.js`, or `test/docs/*.js`.
View all 400+ [contributors](https://github.com/Automattic/mongoose/graphs/contributors).
## Installation
First install [Node.js](https://nodejs.org/) and [MongoDB](https://www.mongodb.org/downloads), then install the `mongoose` package using your preferred package manager:
### Using npm
```sh
npm install mongoose
```
### Using pnpm
```sh
pnpm add mongoose
```
### Using Yarn
```sh
yarn add mongoose
```
### Using Bun
```sh
bun add mongoose
```
Mongoose 6.8.0 also includes alpha support for [Deno](https://deno.land/).
## Importing
```javascript
// Using Node.js `require()`
const mongoose = require('mongoose');
// Using ES6 imports
import mongoose from 'mongoose';
```
Or, using [Deno's `createRequire()` for CommonJS support](https://deno.land/std@0.113.0/node/README.md?source=#commonjs-modules-loading) as follows.
```javascript
import { createRequire } from 'https://deno.land/std@0.177.0/node/module.ts';
const require = createRequire(import.meta.url);
const mongoose = require('mongoose');
mongoose.connect('mongodb://127.0.0.1:27017/test')
.then(() => console.log('Connected!'));
```
You can then run the above script using the following.
```sh
deno run --allow-net --allow-read --allow-sys --allow-env mongoose-test.js
```
## Mongoose for Enterprise
Available as part of the Tidelift Subscription
The maintainers of mongoose and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-mongoose?utm_source=npm-mongoose&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
## Overview
### Connecting to MongoDB
First, we need to define a connection. If your app uses only one database, you should use `mongoose.connect`. If you need to create additional connections, use `mongoose.createConnection`.
Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`.
```js
await mongoose.connect('mongodb://127.0.0.1/my_database');
```
Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`.
**Note:** *If the local connection fails then try using 127.0.0.1 instead of localhost. Sometimes issues may arise when the local hostname has been changed.*
**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc.
### Defining a Model
Models are defined through the `Schema` interface.
```js
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
const BlogPost = new Schema({
author: ObjectId,
title: String,
body: String,
date: Date
});
```
Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of:
* [Validators](https://mongoosejs.com/docs/validation.html) (async and sync)
* [Defaults](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-default)
* [Getters](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-get)
* [Setters](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-set)
* [Indexes](https://mongoosejs.com/docs/guide.html#indexes)
* [Middleware](https://mongoosejs.com/docs/middleware.html)
* [Methods](https://mongoosejs.com/docs/guide.html#methods) definition
* [Statics](https://mongoosejs.com/docs/guide.html#statics) definition
* [Plugins](https://mongoosejs.com/docs/plugins.html)
* [pseudo-JOINs](https://mongoosejs.com/docs/populate.html)
The following example shows some of these features:
```js
const Comment = new Schema({
name: { type: String, default: 'hahaha' },
age: { type: Number, min: 18, index: true },
bio: { type: String, match: /[a-z]/ },
date: { type: Date, default: Date.now },
buff: Buffer
});
// a setter
Comment.path('name').set(function(v) {
return capitalize(v);
});
// middleware
Comment.pre('save', function(next) {
notify(this.get('email'));
next();
});
```
Take a look at the example in [`examples/schema/schema.js`](https://github.com/Automattic/mongoose/blob/master/examples/schema/schema.js) for an end-to-end example of a typical setup.
### Accessing a Model
Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function
```js
const MyModel = mongoose.model('ModelName');
```
Or just do it all at once
```js
const MyModel = mongoose.model('ModelName', mySchema);
```
The first argument is the *singular* name of the collection your model is for. **Mongoose automatically looks for the *plural* version of your model name.** For example, if you use
```js
const MyModel = mongoose.model('Ticket', mySchema);
```
Then `MyModel` will use the **tickets** collection, not the **ticket** collection. For more details read the [model docs](https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-model).
Once we have our model, we can then instantiate it, and save it:
```js
const instance = new MyModel();
instance.my.key = 'hello';
await instance.save();
```
Or we can find documents from the same collection
```js
await MyModel.find({});
```
You can also `findOne`, `findById`, `update`, etc.
```js
const instance = await MyModel.findOne({ /* ... */ });
console.log(instance.my.key); // 'hello'
```
For more details check out [the docs](https://mongoosejs.com/docs/queries.html).
**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created:
```js
const conn = mongoose.createConnection('your connection string');
const MyModel = conn.model('ModelName', schema);
const m = new MyModel();
await m.save(); // works
```
vs
```js
const conn = mongoose.createConnection('your connection string');
const MyModel = mongoose.model('ModelName', schema);
const m = new MyModel();
await m.save(); // does not work b/c the default connection object was never connected
```
### Embedded Documents
In the first example snippet, we defined a key in the Schema that looks like:
```txt
comments: [Comment]
```
Where `Comment` is a `Schema` we created. This means that creating embedded documents is as simple as:
```js
// retrieve my model
const BlogPost = mongoose.model('BlogPost');
// create a blog post
const post = new BlogPost();
// create a comment
post.comments.push({ title: 'My comment' });
await post.save();
```
The same goes for removing them:
```js
const post = await BlogPost.findById(myId);
post.comments[0].deleteOne();
await post.save();
```
Embedded documents enjoy all the same features as your models. Defaults, validators, middleware.
### Middleware
See the [docs](https://mongoosejs.com/docs/middleware.html) page.
#### Intercepting and mutating method arguments
You can intercept method arguments via middleware.
For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value:
```js
schema.pre('set', function(next, path, val, typel) {
// `this` is the current Document
this.emit('set', path, val);
// Pass control to the next pre
next();
});
```
Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`:
```js
schema.pre(method, function firstPre(next, methodArg1, methodArg2) {
// Mutate methodArg1
next('altered-' + methodArg1.toString(), methodArg2);
});
// pre declaration is chainable
schema.pre(method, function secondPre(next, methodArg1, methodArg2) {
console.log(methodArg1);
// => 'altered-originalValOfMethodArg1'
console.log(methodArg2);
// => 'originalValOfMethodArg2'
// Passing no arguments to `next` automatically passes along the current argument values
// i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)`
// and also equivalent to, with the example method arg
// values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')`
next();
});
```
#### Schema gotcha
`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation:
```js
new Schema({
broken: { type: Boolean },
asset: {
name: String,
type: String // uh oh, it broke. asset will be interpreted as String
}
});
new Schema({
works: { type: Boolean },
asset: {
name: String,
type: { type: String } // works. asset is an object with a type property
}
});
```
### Driver Access
Mongoose is built on top of the [official MongoDB Node.js driver](https://github.com/mongodb/node-mongodb-native). Each mongoose model keeps a reference to a [native MongoDB driver collection](http://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html). The collection object can be accessed using `YourModel.collection`. However, using the collection object directly bypasses all mongoose features, including hooks, validation, etc. The one
notable exception that `YourModel.collection` still buffers
commands. As such, `YourModel.collection.find()` will **not**
return a cursor.
## API Docs
[Mongoose API documentation](https://mongoosejs.com/docs/api/mongoose.html), generated using [dox](https://github.com/tj/dox)
and [acquit](https://github.com/vkarpov15/acquit).
## Related Projects
### MongoDB Runners
* [run-rs](https://www.npmjs.com/package/run-rs)
* [mongodb-memory-server](https://www.npmjs.com/package/mongodb-memory-server)
* [mongodb-topology-manager](https://www.npmjs.com/package/mongodb-topology-manager)
### Unofficial CLIs
* [mongoosejs-cli](https://www.npmjs.com/package/mongoosejs-cli)
### Data Seeding
* [dookie](https://www.npmjs.com/package/dookie)
* [seedgoose](https://www.npmjs.com/package/seedgoose)
* [mongoose-data-seed](https://www.npmjs.com/package/mongoose-data-seed)
### Express Session Stores
* [connect-mongodb-session](https://www.npmjs.com/package/connect-mongodb-session)
* [connect-mongo](https://www.npmjs.com/package/connect-mongo)
## License
Copyright (c) 2010 LearnBoost &lt;dev@learnboost.com&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
backend/node_modules/mongoose/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1 @@
Please follow the instructions on [Tidelift's security page](https://tidelift.com/docs/security) to report a security issue.

198
backend/node_modules/mongoose/eslint.config.mjs generated vendored Normal file
View File

@@ -0,0 +1,198 @@
import { defineConfig, globalIgnores } from 'eslint/config';
import mochaNoOnly from 'eslint-plugin-mocha-no-only';
import globals from 'globals';
import tseslint from 'typescript-eslint';
import js from '@eslint/js';
export default defineConfig([
globalIgnores([
'**/tools',
'**/dist',
'test/files/*',
'**/benchmarks',
'**/*.min.js',
'**/docs/js/native.js',
'!**/.*',
'**/node_modules',
'**/.git',
'**/data'
]),
js.configs.recommended,
// general options
{
languageOptions: {
globals: globals.node,
ecmaVersion: 2022, // nodejs 18.0.0,
sourceType: 'commonjs'
},
rules: {
'comma-style': 'error',
indent: ['error', 2, {
SwitchCase: 1,
VariableDeclarator: 2
}],
'keyword-spacing': 'error',
'no-whitespace-before-property': 'error',
'no-buffer-constructor': 'warn',
'no-console': 'off',
'no-constant-condition': 'off',
'no-multi-spaces': 'error',
'func-call-spacing': 'error',
'no-trailing-spaces': 'error',
'no-undef': 'error',
'no-unneeded-ternary': 'error',
'no-const-assign': 'error',
'no-useless-rename': 'error',
'no-dupe-keys': 'error',
'space-in-parens': ['error', 'never'],
'spaced-comment': ['error', 'always', {
block: {
markers: ['!'],
balanced: true
}
}],
'key-spacing': ['error', {
beforeColon: false,
afterColon: true
}],
'comma-spacing': ['error', {
before: false,
after: true
}],
'array-bracket-spacing': 1,
'arrow-spacing': ['error', {
before: true,
after: true
}],
'object-curly-spacing': ['error', 'always'],
'comma-dangle': ['error', 'never'],
'no-unreachable': 'error',
quotes: ['error', 'single'],
'quote-props': ['error', 'as-needed'],
semi: 'error',
'no-extra-semi': 'error',
'semi-spacing': 'error',
'no-spaced-func': 'error',
'no-throw-literal': 'error',
'space-before-blocks': 'error',
'space-before-function-paren': ['error', 'never'],
'space-infix-ops': 'error',
'space-unary-ops': 'error',
'no-var': 'warn',
'prefer-const': 'warn',
strict: ['error', 'global'],
'no-restricted-globals': ['error', {
name: 'context',
message: 'Don\'t use Mocha\'s global context'
}],
'no-prototype-builtins': 'off',
'no-empty': 'off',
'eol-last': 'warn',
'no-multiple-empty-lines': ['warn', {
max: 2
}]
}
},
// general typescript options
{
files: ['**/*.{ts,tsx}', '**/*.md/*.ts', '**/*.md/*.typescript'],
extends: [
tseslint.configs.recommended
],
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: [],
defaultProject: 'tsconfig.json'
}
}
},
rules: {
'@typescript-eslint/triple-slash-reference': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-empty-function': 'off',
'spaced-comment': ['error', 'always', {
block: {
markers: ['!'],
balanced: true
},
markers: ['/']
}],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/prefer-optional-chain': 'error',
'@typescript-eslint/no-dupe-class-members': 'error',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/space-infix-ops': 'off',
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-wrapper-object-types': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-unsafe-function-type': 'off'
}
},
// type test specific options
{
files: ['test/types/**/*.ts'],
rules: {
'@typescript-eslint/no-empty-interface': 'off'
}
},
// test specific options (including type tests)
{
files: ['test/**/*.js', 'test/**/*.ts'],
ignores: ['deno*.mjs'],
plugins: {
'mocha-no-only': mochaNoOnly
},
languageOptions: {
globals: globals.mocha
},
rules: {
'no-self-assign': 'off',
'mocha-no-only/mocha-no-only': ['error']
}
},
// deno specific options
{
files: ['**/deno*.mjs'],
languageOptions: {
globals: {
// "globals" currently has no definition for deno
Deno: 'readonly'
}
}
},
// general options for module files
{
files: ['**/*.mjs'],
languageOptions: {
sourceType: 'module'
}
},
// doc script specific options
{
files: ['**/docs/js/**/*.js'],
languageOptions: {
globals: {
...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
...globals.browser }
}
}
]);

64
backend/node_modules/mongoose/index.js generated vendored Normal file
View File

@@ -0,0 +1,64 @@
/**
* Export lib/mongoose
*
*/
'use strict';
const mongoose = require('./lib/');
module.exports = mongoose;
module.exports.default = mongoose;
module.exports.mongoose = mongoose;
// Re-export for ESM support
module.exports.cast = mongoose.cast;
module.exports.STATES = mongoose.STATES;
module.exports.setDriver = mongoose.setDriver;
module.exports.set = mongoose.set;
module.exports.get = mongoose.get;
module.exports.createConnection = mongoose.createConnection;
module.exports.connect = mongoose.connect;
module.exports.disconnect = mongoose.disconnect;
module.exports.startSession = mongoose.startSession;
module.exports.pluralize = mongoose.pluralize;
module.exports.model = mongoose.model;
module.exports.deleteModel = mongoose.deleteModel;
module.exports.modelNames = mongoose.modelNames;
module.exports.plugin = mongoose.plugin;
module.exports.connections = mongoose.connections;
module.exports.version = mongoose.version;
module.exports.Aggregate = mongoose.Aggregate;
module.exports.Mongoose = mongoose.Mongoose;
module.exports.Schema = mongoose.Schema;
module.exports.SchemaType = mongoose.SchemaType;
module.exports.SchemaTypes = mongoose.SchemaTypes;
module.exports.VirtualType = mongoose.VirtualType;
module.exports.Types = mongoose.Types;
module.exports.Query = mongoose.Query;
module.exports.Model = mongoose.Model;
module.exports.Document = mongoose.Document;
module.exports.ObjectId = mongoose.ObjectId;
module.exports.isValidObjectId = mongoose.isValidObjectId;
module.exports.isObjectIdOrHexString = mongoose.isObjectIdOrHexString;
module.exports.syncIndexes = mongoose.syncIndexes;
module.exports.Decimal128 = mongoose.Decimal128;
module.exports.Mixed = mongoose.Mixed;
module.exports.Date = mongoose.Date;
module.exports.Number = mongoose.Number;
module.exports.Error = mongoose.Error;
module.exports.MongooseError = mongoose.MongooseError;
module.exports.now = mongoose.now;
module.exports.CastError = mongoose.CastError;
module.exports.SchemaTypeOptions = mongoose.SchemaTypeOptions;
module.exports.mongo = mongoose.mongo;
module.exports.mquery = mongoose.mquery;
module.exports.sanitizeFilter = mongoose.sanitizeFilter;
module.exports.trusted = mongoose.trusted;
module.exports.skipMiddlewareFunction = mongoose.skipMiddlewareFunction;
module.exports.overwriteMiddlewareResult = mongoose.overwriteMiddlewareResult;
// The following properties are not exported using ESM because `setDriver()` can mutate these
// module.exports.connection = mongoose.connection;
// module.exports.Collection = mongoose.Collection;
// module.exports.Connection = mongoose.Connection;

1226
backend/node_modules/mongoose/lib/aggregate.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

436
backend/node_modules/mongoose/lib/cast.js generated vendored Normal file
View File

@@ -0,0 +1,436 @@
'use strict';
/*!
* Module dependencies.
*/
const CastError = require('./error/cast');
const StrictModeError = require('./error/strict');
const Types = require('./schema/index');
const cast$expr = require('./helpers/query/cast$expr');
const castString = require('./cast/string');
const castTextSearch = require('./schema/operators/text');
const get = require('./helpers/get');
const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
const isOperator = require('./helpers/query/isOperator');
const util = require('util');
const isObject = require('./helpers/isObject');
const isMongooseObject = require('./helpers/isMongooseObject');
const utils = require('./utils');
const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon'];
/**
* Handles internal casting for query filters.
*
* @param {Schema} schema
* @param {object} obj Object to cast
* @param {object} [options] the query options
* @param {boolean|"throw"} [options.strict] Whether to enable all strict options
* @param {boolean|"throw"} [options.strictQuery] Enable strict Queries
* @param {boolean} [options.sanitizeFilter] avoid adding implicit query selectors ($in)
* @param {boolean} [options.upsert]
* @param {Query} [context] passed to setters
* @api private
*/
module.exports = function cast(schema, obj, options, context) {
if (Array.isArray(obj)) {
throw new Error('Query filter must be an object, got an array ' + util.inspect(obj));
}
if (obj == null) {
return obj;
}
if (schema?.discriminators != null && obj[schema.options.discriminatorKey] != null) {
schema = getSchemaDiscriminatorByValue(schema, obj[schema.options.discriminatorKey]) || schema;
}
const paths = Object.keys(obj);
let i = paths.length;
let _keys;
let any$conditionals;
let schematype;
let nested;
let path;
let type;
let val;
options = options || {};
while (i--) {
path = paths[i];
val = obj[path];
if (path === '$or' || path === '$nor' || path === '$and') {
if (!Array.isArray(val)) {
throw new CastError('Array', val, path);
}
for (let k = val.length - 1; k >= 0; k--) {
if (val[k] == null || typeof val[k] !== 'object') {
throw new CastError('Object', val[k], path + '.' + k);
}
const beforeCastKeysLength = Object.keys(val[k]).length;
const discriminatorValue = val[k][schema.options.discriminatorKey];
if (discriminatorValue == null) {
val[k] = cast(schema, val[k], options, context);
} else {
const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue);
val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context);
}
if (utils.hasOwnKeys(val[k]) === false && beforeCastKeysLength !== 0) {
val.splice(k, 1);
}
}
// delete empty: {$or: []} -> {}
if (val.length === 0) {
delete obj[path];
}
} else if (path === '$where') {
type = typeof val;
if (type !== 'string' && type !== 'function') {
throw new Error('Must have a string or function for $where');
}
if (type === 'function') {
obj[path] = val.toString();
}
continue;
} else if (path === '$expr') {
val = cast$expr(val, schema);
continue;
} else if (path === '$elemMatch') {
val = cast(schema, val, options, context);
} else if (path === '$text') {
val = castTextSearch(val, path);
} else if (path === '$comment' && !Object.hasOwn(schema.paths, '$comment')) {
val = castString(val, path);
obj[path] = val;
} else {
if (!schema) {
// no casting for Mixed types
continue;
}
schematype = schema.path(path);
// Check for embedded discriminator paths
if (!schematype) {
const split = path.split('.');
let j = split.length;
while (j--) {
const pathFirstHalf = split.slice(0, j).join('.');
const pathLastHalf = split.slice(j).join('.');
const _schematype = schema.path(pathFirstHalf);
const discriminatorKey = _schematype?.schema?.options?.discriminatorKey;
// gh-6027: if we haven't found the schematype but this path is
// underneath an embedded discriminator and the embedded discriminator
// key is in the query, use the embedded discriminator schema
if (_schematype?.schema?.discriminators != null &&
discriminatorKey != null &&
pathLastHalf !== discriminatorKey) {
const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey);
const discriminators = _schematype.schema.discriminators;
if (typeof discriminatorVal === 'string' && discriminators[discriminatorVal] != null) {
schematype = discriminators[discriminatorVal].path(pathLastHalf);
} else if (discriminatorVal != null &&
Object.keys(discriminatorVal).length === 1 &&
Array.isArray(discriminatorVal.$in) &&
discriminatorVal.$in.length === 1 &&
typeof discriminatorVal.$in[0] === 'string' &&
discriminators[discriminatorVal.$in[0]] != null) {
schematype = discriminators[discriminatorVal.$in[0]].path(pathLastHalf);
}
}
}
}
if (!schematype) {
// Handle potential embedded array queries
const split = path.split('.');
let j = split.length;
let pathFirstHalf;
let pathLastHalf;
let remainingConds;
// Find the part of the var path that is a path of the Schema
while (j--) {
pathFirstHalf = split.slice(0, j).join('.');
schematype = schema.path(pathFirstHalf);
if (schematype) {
break;
}
}
// If a substring of the input path resolves to an actual real path...
if (schematype) {
// Apply the casting; similar code for $elemMatch in schema/array.js
if (schematype.schema) {
remainingConds = {};
pathLastHalf = split.slice(j).join('.');
remainingConds[pathLastHalf] = val;
const ret = cast(schematype.schema, remainingConds, options, context)[pathLastHalf];
if (ret === void 0) {
delete obj[path];
} else {
obj[path] = ret;
}
} else {
obj[path] = val;
}
continue;
}
if (isObject(val)) {
// handle geo schemas that use object notation
// { loc: { long: Number, lat: Number }
let geo = '';
if (val.$near) {
geo = '$near';
} else if (val.$nearSphere) {
geo = '$nearSphere';
} else if (val.$within) {
geo = '$within';
} else if (val.$geoIntersects) {
geo = '$geoIntersects';
} else if (val.$geoWithin) {
geo = '$geoWithin';
}
if (geo) {
const numbertype = new Types.Number('__QueryCasting__', null, null, schema);
let value = val[geo];
if (val.$maxDistance != null) {
val.$maxDistance = numbertype.castForQuery(
null,
val.$maxDistance,
context
);
}
if (val.$minDistance != null) {
val.$minDistance = numbertype.castForQuery(
null,
val.$minDistance,
context
);
}
if (geo === '$within') {
const withinType = value.$center
|| value.$centerSphere
|| value.$box
|| value.$polygon;
if (!withinType) {
throw new Error('Bad $within parameter: ' + JSON.stringify(val));
}
value = withinType;
} else if (geo === '$near' &&
typeof value.type === 'string' && Array.isArray(value.coordinates)) {
// geojson; cast the coordinates
value = value.coordinates;
} else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') &&
value.$geometry && typeof value.$geometry.type === 'string' &&
Array.isArray(value.$geometry.coordinates)) {
if (value.$maxDistance != null) {
value.$maxDistance = numbertype.castForQuery(
null,
value.$maxDistance,
context
);
}
if (value.$minDistance != null) {
value.$minDistance = numbertype.castForQuery(
null,
value.$minDistance,
context
);
}
if (isMongooseObject(value.$geometry)) {
value.$geometry = value.$geometry.toObject({
transform: false,
virtuals: false
});
}
value = value.$geometry.coordinates;
} else if (geo === '$geoWithin') {
if (value.$geometry) {
if (isMongooseObject(value.$geometry)) {
value.$geometry = value.$geometry.toObject({ virtuals: false });
}
const geoWithinType = value.$geometry.type;
if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) {
throw new Error('Invalid geoJSON type for $geoWithin "' +
geoWithinType + '", must be "Polygon" or "MultiPolygon"');
}
value = value.$geometry.coordinates;
} else {
value = value.$box || value.$polygon || value.$center ||
value.$centerSphere;
if (isMongooseObject(value)) {
value = value.toObject({ virtuals: false });
}
}
}
_cast(value, numbertype, context);
continue;
}
}
if (schema.nested[path]) {
continue;
}
const strict = 'strict' in options ? options.strict : schema.options.strict;
const strictQuery = getStrictQuery(options, schema._userProvidedOptions, schema.options, context);
if (options.upsert && strict) {
if (strict === 'throw') {
throw new StrictModeError(path);
}
throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
'schema, strict mode is `true`, and upsert is `true`.');
} if (strictQuery === 'throw') {
throw new StrictModeError(path, 'Path "' + path + '" is not in ' +
'schema and strictQuery is \'throw\'.');
} else if (strictQuery) {
delete obj[path];
}
} else if (val == null) {
continue;
} else if (utils.isPOJO(val)) {
any$conditionals = Object.keys(val).some(isOperator);
if (!any$conditionals) {
obj[path] = schematype.castForQuery(
null,
val,
context
);
} else {
const ks = Object.keys(val);
let $cond;
let k = ks.length;
while (k--) {
$cond = ks[k];
nested = val[$cond];
if ($cond === '$elemMatch') {
if (nested && schematype?.schema != null) {
cast(schematype.schema, nested, options, context);
} else if (nested && schematype?.$isMongooseArray) {
if (utils.isPOJO(nested) && nested.$not != null) {
cast(schema, nested, options, context);
} else {
val[$cond] = schematype.castForQuery(
$cond,
nested,
context
);
}
}
} else if ($cond === '$not') {
if (nested && schematype) {
_keys = Object.keys(nested);
if (_keys.length && isOperator(_keys[0])) {
for (const key in nested) {
nested[key] = schematype.castForQuery(
key,
nested[key],
context
);
}
} else {
val[$cond] = schematype.castForQuery(
$cond,
nested,
context
);
}
continue;
}
} else {
val[$cond] = schematype.castForQuery(
$cond,
nested,
context
);
}
}
}
} else if (Array.isArray(val) && ['Buffer', 'Array'].indexOf(schematype.instance) === -1 && !options.sanitizeFilter) {
const casted = [];
const valuesArray = val;
for (const _val of valuesArray) {
casted.push(schematype.castForQuery(
null,
_val,
context
));
}
obj[path] = { $in: casted };
} else {
obj[path] = schematype.castForQuery(
null,
val,
context
);
}
}
}
return obj;
};
function _cast(val, numbertype, context) {
if (Array.isArray(val)) {
val.forEach(function(item, i) {
if (Array.isArray(item) || isObject(item)) {
return _cast(item, numbertype, context);
}
val[i] = numbertype.castForQuery(null, item, context);
});
} else {
const nearKeys = Object.keys(val);
let nearLen = nearKeys.length;
while (nearLen--) {
const nkey = nearKeys[nearLen];
const item = val[nkey];
if (Array.isArray(item) || isObject(item)) {
_cast(item, numbertype, context);
val[nkey] = item;
} else {
val[nkey] = numbertype.castForQuery({ val: item, context: context });
}
}
}
}
function getStrictQuery(queryOptions, schemaUserProvidedOptions, schemaOptions, context) {
if ('strictQuery' in queryOptions) {
return queryOptions.strictQuery;
}
if ('strictQuery' in schemaUserProvidedOptions) {
return schemaUserProvidedOptions.strictQuery;
}
const mongooseOptions = context?.mongooseCollection?.conn?.base?.options;
if (mongooseOptions) {
if ('strictQuery' in mongooseOptions) {
return mongooseOptions.strictQuery;
}
}
return schemaOptions.strictQuery;
}

46
backend/node_modules/mongoose/lib/cast/bigint.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
'use strict';
const { Long } = require('mongodb/lib/bson');
/**
* Given a value, cast it to a BigInt, or throw an `Error` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {any} value
* @return {bigint|null|undefined}
* @throws {Error} if `value` is not one of the allowed values
* @api private
*/
const MAX_BIGINT = 9223372036854775807n;
const MIN_BIGINT = -9223372036854775808n;
const ERROR_MESSAGE = `Mongoose only supports BigInts between ${MIN_BIGINT} and ${MAX_BIGINT} because MongoDB does not support arbitrary precision integers`;
module.exports = function castBigInt(val) {
if (val == null) {
return val;
}
if (val === '') {
return null;
}
if (typeof val === 'bigint') {
if (val > MAX_BIGINT || val < MIN_BIGINT) {
throw new Error(ERROR_MESSAGE);
}
return val;
}
if (val instanceof Long) {
return val.toBigInt();
}
if (typeof val === 'string' || typeof val === 'number') {
val = BigInt(val);
if (val > MAX_BIGINT || val < MIN_BIGINT) {
throw new Error(ERROR_MESSAGE);
}
return val;
}
throw new Error(`Cannot convert value to BigInt: "${val}"`);
};

32
backend/node_modules/mongoose/lib/cast/boolean.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
'use strict';
const CastError = require('../error/cast');
/**
* Given a value, cast it to a boolean, or throw a `CastError` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {any} value
* @param {string} [path] optional the path to set on the CastError
* @return {boolean|null|undefined}
* @throws {CastError} if `value` is not one of the allowed values
* @api private
*/
module.exports = function castBoolean(value, path) {
if (module.exports.convertToTrue.has(value)) {
return true;
}
if (module.exports.convertToFalse.has(value)) {
return false;
}
if (value == null) {
return value;
}
throw new CastError('boolean', value, path);
};
module.exports.convertToTrue = new Set([true, 'true', 1, '1', 'yes']);
module.exports.convertToFalse = new Set([false, 'false', 0, '0', 'no']);

41
backend/node_modules/mongoose/lib/cast/date.js generated vendored Normal file
View File

@@ -0,0 +1,41 @@
'use strict';
const assert = require('assert');
module.exports = function castDate(value) {
// Support empty string because of empty form values. Originally introduced
// in https://github.com/Automattic/mongoose/commit/efc72a1898fc3c33a319d915b8c5463a22938dfe
if (value == null || value === '') {
return null;
}
if (value instanceof Date) {
assert.ok(!isNaN(value.valueOf()));
return value;
}
let date;
assert.ok(typeof value !== 'boolean');
if (value instanceof Number || typeof value === 'number') {
date = new Date(value);
} else if (typeof value === 'string' && !isNaN(Number(value)) && (Number(value) >= 275761 || Number(value) < -271820)) {
// string representation of milliseconds take this path
date = new Date(Number(value));
} else if (typeof value.valueOf === 'function') {
// support for moment.js. This is also the path strings will take because
// strings have a `valueOf()`
date = new Date(value.valueOf());
} else {
// fallback
date = new Date(value);
}
if (!isNaN(date.valueOf())) {
return date;
}
assert.ok(false);
};

39
backend/node_modules/mongoose/lib/cast/decimal128.js generated vendored Normal file
View File

@@ -0,0 +1,39 @@
'use strict';
const Decimal128Type = require('../types/decimal128');
const assert = require('assert');
module.exports = function castDecimal128(value) {
if (value == null) {
return value;
}
if (typeof value === 'object' && typeof value.$numberDecimal === 'string') {
return Decimal128Type.fromString(value.$numberDecimal);
}
if (value instanceof Decimal128Type) {
return value;
}
if (typeof value === 'string') {
return Decimal128Type.fromString(value);
}
if (typeof Buffer === 'function' && Buffer.isBuffer(value)) {
return new Decimal128Type(value);
}
if (typeof Uint8Array === 'function' && value instanceof Uint8Array) {
return new Decimal128Type(value);
}
if (typeof value === 'number') {
return Decimal128Type.fromString(String(value));
}
if (typeof value.valueOf === 'function' && typeof value.valueOf() === 'string') {
return Decimal128Type.fromString(value.valueOf());
}
assert.ok(false);
};

50
backend/node_modules/mongoose/lib/cast/double.js generated vendored Normal file
View File

@@ -0,0 +1,50 @@
'use strict';
const assert = require('assert');
const BSON = require('mongodb/lib/bson');
const isBsonType = require('../helpers/isBsonType');
/**
* Given a value, cast it to a IEEE 754-2008 floating point, or throw an `Error` if the value
* cannot be casted. `null`, `undefined`, and `NaN` are considered valid inputs.
*
* @param {any} value
* @return {number}
* @throws {Error} if `value` does not represent a IEEE 754-2008 floating point. If casting from a string, see [BSON Double.fromString API documentation](https://mongodb.github.io/node-mongodb-native/Next/classes/BSON.Double.html#fromString)
* @api private
*/
module.exports = function castDouble(val) {
if (val == null || val === '') {
return null;
}
let coercedVal;
if (isBsonType(val, 'Long')) {
coercedVal = val.toNumber();
} else if (typeof val === 'string') {
try {
coercedVal = BSON.Double.fromString(val);
return coercedVal;
} catch {
assert.ok(false);
}
} else if (typeof val === 'object') {
const tempVal = val.valueOf() ?? val.toString();
// ex: { a: 'im an object, valueOf: () => 'helloworld' } // throw an error
if (typeof tempVal === 'string') {
try {
coercedVal = BSON.Double.fromString(tempVal);
return coercedVal;
} catch {
assert.ok(false);
}
} else {
coercedVal = Number(tempVal);
}
} else {
coercedVal = Number(val);
}
return new BSON.Double(coercedVal);
};

36
backend/node_modules/mongoose/lib/cast/int32.js generated vendored Normal file
View File

@@ -0,0 +1,36 @@
'use strict';
const isBsonType = require('../helpers/isBsonType');
const assert = require('assert');
/**
* Given a value, cast it to a Int32, or throw an `Error` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {any} value
* @return {number}
* @throws {Error} if `value` does not represent an integer, or is outside the bounds of an 32-bit integer.
* @api private
*/
module.exports = function castInt32(val) {
if (val == null) {
return val;
}
if (val === '') {
return null;
}
const coercedVal = isBsonType(val, 'Long') ? val.toNumber() : Number(val);
const INT32_MAX = 0x7FFFFFFF;
const INT32_MIN = -0x80000000;
if (coercedVal === (coercedVal | 0) &&
coercedVal >= INT32_MIN &&
coercedVal <= INT32_MAX
) {
return coercedVal;
}
assert.ok(false);
};

42
backend/node_modules/mongoose/lib/cast/number.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
'use strict';
/**
* Given a value, cast it to a number, or throw an `Error` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {any} value
* @return {number}
* @throws {Error} if `value` is not one of the allowed values
* @api private
*/
module.exports = function castNumber(val) {
if (val == null) {
return val;
}
if (val === '') {
return null;
}
if (typeof val === 'string' || typeof val === 'boolean') {
val = Number(val);
}
if (isNaN(val)) {
throw new Error('Cast to Number failed: value is not a valid number');
}
if (val instanceof Number) {
return val.valueOf();
}
if (typeof val === 'number') {
return val;
}
if (!Array.isArray(val) && typeof val.valueOf === 'function') {
return Number(val.valueOf());
}
if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) {
return Number(val);
}
throw new Error('Cast to Number failed: value is not a valid number');
};

29
backend/node_modules/mongoose/lib/cast/objectid.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
'use strict';
const isBsonType = require('../helpers/isBsonType');
const ObjectId = require('../types/objectid');
module.exports = function castObjectId(value) {
if (value == null) {
return value;
}
if (isBsonType(value, 'ObjectId')) {
return value;
}
if (value._id) {
if (isBsonType(value._id, 'ObjectId')) {
return value._id;
}
if (value._id.toString instanceof Function) {
return new ObjectId(value._id.toString());
}
}
if (value.toString instanceof Function) {
return new ObjectId(value.toString());
}
return new ObjectId(value);
};

37
backend/node_modules/mongoose/lib/cast/string.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
'use strict';
const CastError = require('../error/cast');
/**
* Given a value, cast it to a string, or throw a `CastError` if the value
* cannot be casted. `null` and `undefined` are considered valid.
*
* @param {any} value
* @param {string} [path] optional the path to set on the CastError
* @return {string|null|undefined}
* @throws {CastError}
* @api private
*/
module.exports = function castString(value, path) {
// If null or undefined
if (value == null) {
return value;
}
// handle documents being passed
if (typeof value?._id === 'string') {
return value._id;
}
// Re: gh-647 and gh-3030, we're ok with casting using `toString()`
// **unless** its the default Object.toString, because "[object Object]"
// doesn't really qualify as useful data
if (value.toString &&
value.toString !== Object.prototype.toString &&
!Array.isArray(value)) {
return value.toString();
}
throw new CastError('string', value, path);
};

35
backend/node_modules/mongoose/lib/cast/uuid.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
'use strict';
const UUID = require('mongodb/lib/bson').UUID;
const UUID_FORMAT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i;
module.exports = function castUUID(value) {
if (value == null) {
return value;
}
if (value instanceof UUID) {
return value;
}
if (typeof value === 'string') {
if (UUID_FORMAT.test(value)) {
return new UUID(value);
} else {
throw new Error(`"${value}" is not a valid UUID string`);
}
}
// Re: gh-647 and gh-3030, we're ok with casting using `toString()`
// **unless** its the default Object.toString, because "[object Object]"
// doesn't really qualify as useful data
if (value.toString && value.toString !== Object.prototype.toString) {
if (UUID_FORMAT.test(value.toString())) {
return new UUID(value.toString());
}
}
throw new Error(`"${value}" cannot be casted to a UUID`);
};
module.exports.UUID_FORMAT = UUID_FORMAT;

321
backend/node_modules/mongoose/lib/collection.js generated vendored Normal file
View File

@@ -0,0 +1,321 @@
'use strict';
/*!
* Module dependencies.
*/
const EventEmitter = require('events').EventEmitter;
const STATES = require('./connectionState');
const immediate = require('./helpers/immediate');
/**
* Abstract Collection constructor
*
* This is the base class that drivers inherit from and implement.
*
* @param {string} name name of the collection
* @param {Connection} conn A MongooseConnection instance
* @param {object} [opts] optional collection options
* @api public
*/
function Collection(name, conn, opts) {
if (opts === void 0) {
opts = {};
}
this.opts = opts;
this.name = name;
this.collectionName = name;
this.conn = conn;
this.queue = [];
this.buffer = !conn?._hasOpened;
this.emitter = new EventEmitter();
if (STATES.connected === this.conn.readyState) {
this.onOpen();
}
}
/**
* The collection name
*
* @api public
* @property name
*/
Collection.prototype.name;
/**
* The collection name
*
* @api public
* @property collectionName
*/
Collection.prototype.collectionName;
/**
* The Connection instance
*
* @api public
* @property conn
*/
Collection.prototype.conn;
/**
* Called when the database connects
*
* @api private
*/
Collection.prototype.onOpen = function() {
this.buffer = false;
immediate(() => this.doQueue());
};
/**
* Called when the database disconnects
*
* @api private
*/
Collection.prototype.onClose = function() {};
/**
* Queues a method for later execution when its
* database connection opens.
*
* @param {string} name name of the method to queue
* @param {Array} args arguments to pass to the method when executed
* @api private
*/
Collection.prototype.addQueue = function(name, args) {
this.queue.push([name, args]);
return this;
};
/**
* Removes a queued method
*
* @param {string} name name of the method to queue
* @param {Array} args arguments to pass to the method when executed
* @api private
*/
Collection.prototype.removeQueue = function(name, args) {
const index = this.queue.findIndex(v => v[0] === name && v[1] === args);
if (index === -1) {
return false;
}
this.queue.splice(index, 1);
return true;
};
/**
* Executes all queued methods and clears the queue.
*
* @api private
*/
Collection.prototype.doQueue = function() {
for (const method of this.queue) {
if (typeof method[0] === 'function') {
method[0].apply(this, method[1]);
} else {
this[method[0]].apply(this, method[1]);
}
}
this.queue = [];
const _this = this;
immediate(function() {
_this.emitter.emit('queue');
});
return this;
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.ensureIndex = function() {
throw new Error('Collection#ensureIndex unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.createIndex = function() {
throw new Error('Collection#createIndex unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.findAndModify = function() {
throw new Error('Collection#findAndModify unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.findOneAndUpdate = function() {
throw new Error('Collection#findOneAndUpdate unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.findOneAndDelete = function() {
throw new Error('Collection#findOneAndDelete unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.findOneAndReplace = function() {
throw new Error('Collection#findOneAndReplace unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.findOne = function() {
throw new Error('Collection#findOne unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.find = function() {
throw new Error('Collection#find unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.insert = function() {
throw new Error('Collection#insert unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.insertOne = function() {
throw new Error('Collection#insertOne unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.insertMany = function() {
throw new Error('Collection#insertMany unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.save = function() {
throw new Error('Collection#save unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.updateOne = function() {
throw new Error('Collection#updateOne unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.updateMany = function() {
throw new Error('Collection#updateMany unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.deleteOne = function() {
throw new Error('Collection#deleteOne unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.deleteMany = function() {
throw new Error('Collection#deleteMany unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.getIndexes = function() {
throw new Error('Collection#getIndexes unimplemented by driver');
};
/**
* Abstract method that drivers must implement.
*/
Collection.prototype.watch = function() {
throw new Error('Collection#watch unimplemented by driver');
};
/*!
* ignore
*/
Collection.prototype._shouldBufferCommands = function _shouldBufferCommands() {
const opts = this.opts;
if (opts.bufferCommands != null) {
return opts.bufferCommands;
}
if (opts?.schemaUserProvidedOptions?.bufferCommands != null) {
return opts.schemaUserProvidedOptions.bufferCommands;
}
return this.conn._shouldBufferCommands();
};
/*!
* ignore
*/
Collection.prototype._getBufferTimeoutMS = function _getBufferTimeoutMS() {
const conn = this.conn;
const opts = this.opts;
if (opts.bufferTimeoutMS != null) {
return opts.bufferTimeoutMS;
}
if (opts?.schemaUserProvidedOptions?.bufferTimeoutMS != null) {
return opts.schemaUserProvidedOptions.bufferTimeoutMS;
}
return conn._getBufferTimeoutMS();
};
/*!
* Module exports.
*/
module.exports = Collection;

1841
backend/node_modules/mongoose/lib/connection.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

26
backend/node_modules/mongoose/lib/connectionState.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
/*!
* Connection states
*/
'use strict';
const STATES = module.exports = exports = Object.create(null);
const disconnected = 'disconnected';
const connected = 'connected';
const connecting = 'connecting';
const disconnecting = 'disconnecting';
const uninitialized = 'uninitialized';
STATES[0] = disconnected;
STATES[1] = connected;
STATES[2] = connecting;
STATES[3] = disconnecting;
STATES[99] = uninitialized;
STATES[disconnected] = 0;
STATES[connected] = 1;
STATES[connecting] = 2;
STATES[disconnecting] = 3;
STATES[uninitialized] = 99;

73
backend/node_modules/mongoose/lib/constants.js generated vendored Normal file
View File

@@ -0,0 +1,73 @@
'use strict';
/*!
* ignore
*/
const queryOperations = Object.freeze([
// Read
'countDocuments',
'distinct',
'estimatedDocumentCount',
'find',
'findOne',
// Update
'findOneAndReplace',
'findOneAndUpdate',
'replaceOne',
'updateMany',
'updateOne',
// Delete
'deleteMany',
'deleteOne',
'findOneAndDelete'
]);
exports.queryOperations = queryOperations;
/*!
* ignore
*/
const queryMiddlewareFunctions = queryOperations.concat([
'validate'
]);
exports.queryMiddlewareFunctions = queryMiddlewareFunctions;
/*!
* ignore
*/
const aggregateMiddlewareFunctions = [
'aggregate'
];
exports.aggregateMiddlewareFunctions = aggregateMiddlewareFunctions;
/*!
* ignore
*/
const modelMiddlewareFunctions = [
'bulkWrite',
'createCollection',
'insertMany'
];
exports.modelMiddlewareFunctions = modelMiddlewareFunctions;
/*!
* ignore
*/
const documentMiddlewareFunctions = [
'validate',
'save',
'remove',
'updateOne',
'deleteOne',
'init'
];
exports.documentMiddlewareFunctions = documentMiddlewareFunctions;

View File

@@ -0,0 +1,466 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('../error/mongooseError');
const Readable = require('stream').Readable;
const eachAsync = require('../helpers/cursor/eachAsync');
const immediate = require('../helpers/immediate');
const kareem = require('kareem');
const util = require('util');
/**
* An AggregationCursor is a concurrency primitive for processing aggregation
* results one document at a time. It is analogous to QueryCursor.
*
* An AggregationCursor fulfills the Node.js streams3 API,
* in addition to several other mechanisms for loading documents from MongoDB
* one at a time.
*
* Creating an AggregationCursor executes the model's pre aggregate hooks,
* but **not** the model's post aggregate hooks.
*
* Unless you're an advanced user, do **not** instantiate this class directly.
* Use [`Aggregate#cursor()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.cursor()) instead.
*
* @param {Aggregate} agg
* @inherits Readable https://nodejs.org/api/stream.html#class-streamreadable
* @event `cursor`: Emitted when the cursor is created
* @event `error`: Emitted when an error occurred
* @event `data`: Emitted when the stream is flowing and the next doc is ready
* @event `end`: Emitted when the stream is exhausted
* @api public
*/
function AggregationCursor(agg) {
// set autoDestroy=true because on node 12 it's by default false
// gh-10902 need autoDestroy to destroy correctly and emit 'close' event
Readable.call(this, { autoDestroy: true, objectMode: true });
this.cursor = null;
this.agg = agg;
this._transforms = [];
const connection = agg._connection;
const model = agg._model;
delete agg.options.cursor.useMongooseAggCursor;
this._mongooseOptions = {};
if (connection) {
this.cursor = connection.db.aggregate(agg._pipeline, agg.options || {});
setImmediate(() => this.emit('cursor', this.cursor));
} else {
_init(model, this, agg);
}
}
util.inherits(AggregationCursor, Readable);
/*!
* ignore
*/
function _init(model, c, agg) {
if (!model.collection.buffer) {
model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err));
} else {
model.collection.emitter.once('queue', function() {
model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err));
});
}
function onPreComplete(err) {
if (err != null) {
_handlePreHookError(c, err);
return;
}
if (typeof agg.options?.cursor?.transform === 'function') {
c._transforms.push(agg.options.cursor.transform);
}
c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {});
c.emit('cursor', c.cursor);
}
}
/**
* Handles error emitted from pre middleware. In particular, checks for `skipWrappedFunction`, which allows skipping
* the actual aggregation and overwriting the function's return value. Because aggregation cursors don't return a value,
* we need to make sure the user doesn't accidentally set a value in skipWrappedFunction.
*
* @param {QueryCursor} queryCursor
* @param {Error} err
* @returns
*/
function _handlePreHookError(queryCursor, err) {
if (err instanceof kareem.skipWrappedFunction) {
const resultValue = err.args[0];
if (resultValue != null && (!Array.isArray(resultValue) || resultValue.length)) {
const err = new MongooseError(
'Cannot `skipMiddlewareFunction()` with a value when using ' +
'`.aggregate().cursor()`, value must be nullish or empty array, got "' +
util.inspect(resultValue) +
'".'
);
queryCursor._markError(err);
queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err);
return;
}
queryCursor.emit('cursor', null);
return;
}
queryCursor._markError(err);
queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err);
}
/**
* Necessary to satisfy the Readable API
* @method _read
* @memberOf AggregationCursor
* @instance
* @api private
*/
AggregationCursor.prototype._read = function() {
const _this = this;
_next(this, function(error, doc) {
if (error) {
return _this.emit('error', error);
}
if (!doc) {
_this.push(null);
_this.cursor.close(function(error) {
if (error) {
return _this.emit('error', error);
}
});
return;
}
_this.push(doc);
});
};
if (Symbol.asyncIterator != null) {
const msg = 'Mongoose does not support using async iterators with an ' +
'existing aggregation cursor. See https://bit.ly/mongoose-async-iterate-aggregation';
AggregationCursor.prototype[Symbol.asyncIterator] = function() {
throw new MongooseError(msg);
};
}
/**
* Registers a transform function which subsequently maps documents retrieved
* via the streams interface or `.next()`
*
* #### Example:
*
* // Map documents returned by `data` events
* Thing.
* find({ name: /^hello/ }).
* cursor().
* map(function (doc) {
* doc.foo = "bar";
* return doc;
* })
* on('data', function(doc) { console.log(doc.foo); });
*
* // Or map documents returned by `.next()`
* const cursor = Thing.find({ name: /^hello/ }).
* cursor().
* map(function (doc) {
* doc.foo = "bar";
* return doc;
* });
* cursor.next(function(error, doc) {
* console.log(doc.foo);
* });
*
* @param {Function} fn
* @return {AggregationCursor}
* @memberOf AggregationCursor
* @api public
* @method map
*/
Object.defineProperty(AggregationCursor.prototype, 'map', {
value: function(fn) {
this._transforms.push(fn);
return this;
},
enumerable: true,
configurable: true,
writable: true
});
/**
* Marks this cursor as errored
* @method _markError
* @instance
* @memberOf AggregationCursor
* @api private
*/
AggregationCursor.prototype._markError = function(error) {
this._error = error;
return this;
};
/**
* Marks this cursor as closed. Will stop streaming and subsequent calls to
* `next()` will error.
*
* @return {Promise}
* @api public
* @method close
* @emits "close"
* @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close
*/
AggregationCursor.prototype.close = async function close() {
if (typeof arguments[0] === 'function') {
throw new MongooseError('AggregationCursor.prototype.close() no longer accepts a callback');
}
try {
await this.cursor.close();
} catch (error) {
this.listeners('error').length > 0 && this.emit('error', error);
throw error;
}
this.emit('close');
};
/**
* Marks this cursor as destroyed. Will stop streaming and subsequent calls to
* `next()` will error.
*
* @return {this}
* @api private
* @method _destroy
*/
AggregationCursor.prototype._destroy = function _destroy(_err, callback) {
let waitForCursor = null;
if (!this.cursor) {
waitForCursor = new Promise((resolve) => {
this.once('cursor', resolve);
});
} else {
waitForCursor = Promise.resolve();
}
waitForCursor
.then(() => this.cursor.close())
.then(() => {
this._closed = true;
callback();
})
.catch(error => {
callback(error);
});
return this;
};
/**
* Get the next document from this cursor. Will return `null` when there are
* no documents left.
*
* @return {Promise}
* @api public
* @method next
*/
AggregationCursor.prototype.next = async function next() {
if (typeof arguments[0] === 'function') {
throw new MongooseError('AggregationCursor.prototype.next() no longer accepts a callback');
}
return new Promise((resolve, reject) => {
_next(this, (err, res) => {
if (err != null) {
return reject(err);
}
resolve(res);
});
});
};
/**
* Execute `fn` for every document in the cursor. If `fn` returns a promise,
* will wait for the promise to resolve before iterating on to the next one.
* Returns a promise that resolves when done.
*
* @param {Function} fn
* @param {object} [options]
* @param {number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
* @param {number} [options.batchSize=null] if set, Mongoose will call `fn` with an array of at most `batchSize` documents, instead of a single document
* @param {boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error.
* @return {Promise}
* @api public
* @method eachAsync
*/
AggregationCursor.prototype.eachAsync = function(fn, opts) {
if (typeof arguments[2] === 'function') {
throw new MongooseError('AggregationCursor.prototype.eachAsync() no longer accepts a callback');
}
const _this = this;
if (typeof opts === 'function') {
opts = {};
}
opts = opts || {};
return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts);
};
/**
* Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js)
* You do not need to call this function explicitly, the JavaScript runtime
* will call it for you.
*
* #### Example:
*
* // Async iterator without explicitly calling `cursor()`. Mongoose still
* // creates an AggregationCursor instance internally.
* const agg = Model.aggregate([{ $match: { age: { $gte: 25 } } }]);
* for await (const doc of agg) {
* console.log(doc.name);
* }
*
* // You can also use an AggregationCursor instance for async iteration
* const cursor = Model.aggregate([{ $match: { age: { $gte: 25 } } }]).cursor();
* for await (const doc of cursor) {
* console.log(doc.name);
* }
*
* Node.js 10.x supports async iterators natively without any flags. You can
* enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
*
* **Note:** This function is not set if `Symbol.asyncIterator` is undefined. If
* `Symbol.asyncIterator` is undefined, that means your Node.js version does not
* support async iterators.
*
* @method [Symbol.asyncIterator]
* @memberOf AggregationCursor
* @instance
* @api public
*/
if (Symbol.asyncIterator != null) {
AggregationCursor.prototype[Symbol.asyncIterator] = function() {
return this.transformNull()._transformForAsyncIterator();
};
}
/*!
* ignore
*/
AggregationCursor.prototype._transformForAsyncIterator = function() {
if (this._transforms.indexOf(_transformForAsyncIterator) === -1) {
this.map(_transformForAsyncIterator);
}
return this;
};
/*!
* ignore
*/
AggregationCursor.prototype.transformNull = function(val) {
if (arguments.length === 0) {
val = true;
}
this._mongooseOptions.transformNull = val;
return this;
};
/*!
* ignore
*/
function _transformForAsyncIterator(doc) {
return doc == null ? { done: true } : { value: doc, done: false };
}
/**
* Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#addCursorFlag).
* Useful for setting the `noCursorTimeout` and `tailable` flags.
*
* @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
* @param {boolean} value
* @return {AggregationCursor} this
* @api public
* @method addCursorFlag
*/
AggregationCursor.prototype.addCursorFlag = function(flag, value) {
const _this = this;
_waitForCursor(this, function() {
_this.cursor.addCursorFlag(flag, value);
});
return this;
};
/*!
* ignore
*/
function _waitForCursor(ctx, cb) {
if (ctx.cursor) {
return cb();
}
ctx.once('cursor', function() {
cb();
});
}
/**
* Get the next doc from the underlying cursor and mongooseify it
* (populate, etc.)
* @param {any} ctx
* @param {Function} cb
* @api private
*/
function _next(ctx, cb) {
let callback = cb;
if (ctx._transforms.length) {
callback = function(err, doc) {
if (err || (doc === null && !ctx._mongooseOptions.transformNull)) {
return cb(err, doc);
}
cb(err, ctx._transforms.reduce(function(doc, fn) {
return fn(doc);
}, doc));
};
}
if (ctx._error) {
return immediate(function() {
callback(ctx._error);
});
}
if (ctx.cursor) {
return ctx.cursor.next().then(
doc => {
if (!doc) {
return callback(null, null);
}
callback(null, doc);
},
err => callback(err)
);
} else {
ctx.once('error', cb);
ctx.once('cursor', function() {
_next(ctx, cb);
});
}
}
module.exports = AggregationCursor;

View File

@@ -0,0 +1,218 @@
'use strict';
/*!
* Module dependencies.
*/
const EventEmitter = require('events').EventEmitter;
const MongooseError = require('../error/mongooseError');
/*!
* ignore
*/
const driverChangeStreamEvents = ['close', 'change', 'end', 'error', 'resumeTokenChanged'];
/*!
* ignore
*/
class ChangeStream extends EventEmitter {
constructor(changeStreamPromise, pipeline, options) {
super();
this.driverChangeStream = null;
this.closed = false;
this.bindedEvents = false;
this.pipeline = pipeline;
this.options = options;
this.errored = false;
if (options?.hydrate && !options.model) {
throw new Error(
'Cannot create change stream with `hydrate: true` ' +
'unless calling `Model.watch()`'
);
}
this.$driverChangeStreamPromise = changeStreamPromise.then(
driverChangeStream => {
this.driverChangeStream = driverChangeStream;
// Use setImmediate so the stream pump (_read) has a chance to run and
// the driver cursor initializes before 'ready' resolves. Without this,
// changes emitted immediately after 'ready' can be missed because the
// underlying cursor hasn't sent its initial aggregate to MongoDB yet.
setImmediate(() => this.emit('ready'));
return this;
},
err => {
this.errored = true;
this.emit('error', err);
throw err;
}
);
}
_bindEvents() {
if (this.bindedEvents) {
return;
}
this.bindedEvents = true;
if (this.driverChangeStream == null) {
this.$driverChangeStreamPromise.then(
() => {
this.driverChangeStream.on('close', () => {
this.closed = true;
});
driverChangeStreamEvents.forEach(ev => {
this.driverChangeStream.on(ev, data => {
if (data?.fullDocument != null && this.options?.hydrate) {
data.fullDocument = this.options.model.hydrate(data.fullDocument);
}
this.emit(ev, data);
});
});
},
() => {} // No need to register events if opening change stream failed
);
return;
}
this.driverChangeStream.on('close', () => {
this.closed = true;
});
driverChangeStreamEvents.forEach(ev => {
this.driverChangeStream.on(ev, data => {
if (data?.fullDocument != null && this.options?.hydrate) {
data.fullDocument = this.options.model.hydrate(data.fullDocument);
}
this.emit(ev, data);
});
});
}
hasNext(cb) {
if (this.errored) {
throw new MongooseError('Cannot call hasNext() on errored ChangeStream');
}
if (this.driverChangeStream != null) {
return this.driverChangeStream.hasNext(cb);
}
return this.$driverChangeStreamPromise.then(
() => this.driverChangeStream.hasNext(cb),
err => {
if (cb != null) {
return cb(err);
}
throw err;
}
);
}
next(cb) {
if (this.errored) {
throw new MongooseError('Cannot call next() on errored ChangeStream');
}
if (this.options?.hydrate) {
if (cb != null) {
const originalCb = cb;
cb = (err, data) => {
if (err != null) {
return originalCb(err);
}
if (data.fullDocument != null) {
data.fullDocument = this.options.model.hydrate(data.fullDocument);
}
return originalCb(null, data);
};
}
let maybePromise;
if (this.driverChangeStream != null) {
maybePromise = this.driverChangeStream.next(cb);
} else {
maybePromise = this.$driverChangeStreamPromise.then(
() => this.driverChangeStream.next(cb),
err => {
if (cb != null) {
return cb(err);
}
throw err;
}
);
}
if (typeof maybePromise?.then === 'function') {
maybePromise = maybePromise.then(data => {
if (data.fullDocument != null) {
data.fullDocument = this.options.model.hydrate(data.fullDocument);
}
return data;
});
}
return maybePromise;
}
if (this.driverChangeStream != null) {
return this.driverChangeStream.next(cb);
}
return this.$driverChangeStreamPromise.then(
() => this.driverChangeStream.next(cb),
err => {
if (cb != null) {
return cb(err);
}
throw err;
}
);
}
addListener(event, handler) {
if (this.errored) {
throw new MongooseError('Cannot call addListener() on errored ChangeStream');
}
this._bindEvents();
return super.addListener(event, handler);
}
on(event, handler) {
if (this.errored) {
throw new MongooseError('Cannot call on() on errored ChangeStream');
}
this._bindEvents();
return super.on(event, handler);
}
once(event, handler) {
if (this.errored) {
throw new MongooseError('Cannot call once() on errored ChangeStream');
}
this._bindEvents();
return super.once(event, handler);
}
close() {
this.closed = true;
if (this.driverChangeStream) {
return this.driverChangeStream.close();
} else {
return this.$driverChangeStreamPromise.then(
() => this.driverChangeStream.close(),
() => {} // No need to close if opening the change stream failed
);
}
}
}
/*!
* ignore
*/
module.exports = ChangeStream;

625
backend/node_modules/mongoose/lib/cursor/queryCursor.js generated vendored Normal file
View File

@@ -0,0 +1,625 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('../error/mongooseError');
const Readable = require('stream').Readable;
const eachAsync = require('../helpers/cursor/eachAsync');
const helpers = require('../queryHelpers');
const kareem = require('kareem');
const immediate = require('../helpers/immediate');
const { once } = require('events');
const util = require('util');
/**
* A QueryCursor is a concurrency primitive for processing query results
* one document at a time. A QueryCursor fulfills the Node.js streams3 API,
* in addition to several other mechanisms for loading documents from MongoDB
* one at a time.
*
* QueryCursors execute the model's pre `find` hooks before loading any documents
* from MongoDB, and the model's post `find` hooks after loading each document.
*
* Unless you're an advanced user, do **not** instantiate this class directly.
* Use [`Query#cursor()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.cursor()) instead.
*
* @param {Query} query
* @param {object} options query options passed to `.find()`
* @inherits Readable https://nodejs.org/api/stream.html#class-streamreadable
* @event `cursor`: Emitted when the cursor is created
* @event `error`: Emitted when an error occurred
* @event `data`: Emitted when the stream is flowing and the next doc is ready
* @event `end`: Emitted when the stream is exhausted
* @api public
*/
function QueryCursor(query) {
// set autoDestroy=true because on node 12 it's by default false
// gh-10902 need autoDestroy to destroy correctly and emit 'close' event
Readable.call(this, { autoDestroy: true, objectMode: true });
this.cursor = null;
this.skipped = false;
this.query = query;
this._closed = false;
const model = query.model;
this._mongooseOptions = {};
this._transforms = [];
this.model = model;
this.options = {};
const onPreComplete = (err) => {
if (err != null) {
if (err instanceof kareem.skipWrappedFunction) {
const resultValue = err.args[0];
if (resultValue != null && (!Array.isArray(resultValue) || resultValue.length)) {
const err = new MongooseError(
'Cannot `skipMiddlewareFunction()` with a value when using ' +
'`.find().cursor()`, value must be nullish or empty array, got "' +
util.inspect(resultValue) +
'".'
);
this._markError(err);
this.listeners('error').length > 0 && this.emit('error', err);
return;
}
this.skipped = true;
this.emit('cursor', null);
return;
}
this._markError(err);
this.listeners('error').length > 0 && this.emit('error', err);
return;
}
Object.assign(this.options, query._optionsForExec());
this._transforms = this._transforms.concat(query._transforms.slice());
if (this.options.transform) {
this._transforms.push(this.options.transform);
}
// Re: gh-8039, you need to set the `cursor.batchSize` option, top-level
// `batchSize` option doesn't work.
if (this.options.batchSize) {
// Max out the number of documents we'll populate in parallel at 5000.
this.options._populateBatchSize = Math.min(this.options.batchSize, 5000);
}
if (query._mongooseOptions._asyncIterator) {
this._mongooseOptions._asyncIterator = true;
}
if (model.collection._shouldBufferCommands() && model.collection.buffer) {
model.collection.queue.push([
() => _getRawCursor(query, this)
]);
} else {
_getRawCursor(query, this);
}
};
model.hooks.execPre('find', query).then(() => onPreComplete(null), err => onPreComplete(err));
}
util.inherits(QueryCursor, Readable);
/*!
* ignore
*/
function _getRawCursor(query, queryCursor) {
try {
const cursor = query.model.collection.find(query._conditions, queryCursor.options);
queryCursor.cursor = cursor;
queryCursor.emit('cursor', cursor);
} catch (err) {
queryCursor._markError(err);
queryCursor.listeners('error').length > 0 && queryCursor.emit('error', queryCursor._error);
}
}
/**
* Necessary to satisfy the Readable API
* @method _read
* @memberOf QueryCursor
* @instance
* @api private
*/
QueryCursor.prototype._read = function() {
_next(this, (error, doc) => {
if (error) {
return this.emit('error', error);
}
if (!doc) {
this.push(null);
this.cursor.close((error) => {
if (error) {
return this.emit('error', error);
}
});
return;
}
this.push(doc);
});
};
/**
* Returns the underlying cursor from the MongoDB Node driver that this cursor uses.
*
* @method getDriverCursor
* @memberOf QueryCursor
* @returns {Cursor} MongoDB Node driver cursor instance
* @instance
* @api public
*/
QueryCursor.prototype.getDriverCursor = async function getDriverCursor() {
if (this.cursor) {
return this.cursor;
}
await once(this, 'cursor');
return this.cursor;
};
/**
* Registers a transform function which subsequently maps documents retrieved
* via the streams interface or `.next()`
*
* #### Example:
*
* // Map documents returned by `data` events
* Thing.
* find({ name: /^hello/ }).
* cursor().
* map(function (doc) {
* doc.foo = "bar";
* return doc;
* })
* on('data', function(doc) { console.log(doc.foo); });
*
* // Or map documents returned by `.next()`
* const cursor = Thing.find({ name: /^hello/ }).
* cursor().
* map(function (doc) {
* doc.foo = "bar";
* return doc;
* });
* cursor.next(function(error, doc) {
* console.log(doc.foo);
* });
*
* @param {Function} fn
* @return {QueryCursor}
* @memberOf QueryCursor
* @api public
* @method map
*/
Object.defineProperty(QueryCursor.prototype, 'map', {
value: function(fn) {
this._transforms.push(fn);
return this;
},
enumerable: true,
configurable: true,
writable: true
});
/**
* Marks this cursor as errored
* @method _markError
* @memberOf QueryCursor
* @instance
* @api private
*/
QueryCursor.prototype._markError = function(error) {
this._error = error;
return this;
};
/**
* Marks this cursor as closed. Will stop streaming and subsequent calls to
* `next()` will error.
*
* @return {Promise}
* @api public
* @method close
* @emits close
* @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close
*/
QueryCursor.prototype.close = async function close() {
if (typeof arguments[0] === 'function') {
throw new MongooseError('QueryCursor.prototype.close() no longer accepts a callback');
}
try {
await this.cursor.close();
this._closed = true;
this.emit('close');
} catch (error) {
this.listeners('error').length > 0 && this.emit('error', error);
throw error;
}
};
/**
* Marks this cursor as destroyed. Will stop streaming and subsequent calls to
* `next()` will error.
*
* @return {this}
* @api private
* @method _destroy
*/
QueryCursor.prototype._destroy = function _destroy(_err, callback) {
let waitForCursor = null;
if (!this.cursor) {
waitForCursor = new Promise((resolve) => {
this.once('cursor', resolve);
});
} else {
waitForCursor = Promise.resolve();
}
waitForCursor
.then(() => {
this.cursor.close();
})
.then(() => {
this._closed = true;
callback();
})
.catch(error => {
callback(error);
});
return this;
};
/**
* Rewind this cursor to its uninitialized state. Any options that are present on the cursor will
* remain in effect. Iterating this cursor will cause new queries to be sent to the server, even
* if the resultant data has already been retrieved by this cursor.
*
* @return {AggregationCursor} this
* @api public
* @method rewind
*/
QueryCursor.prototype.rewind = function() {
_waitForCursor(this, () => {
this.cursor.rewind();
});
return this;
};
/**
* Get the next document from this cursor. Will return `null` when there are
* no documents left.
*
* @return {Promise}
* @api public
* @method next
*/
QueryCursor.prototype.next = async function next() {
if (typeof arguments[0] === 'function') {
throw new MongooseError('QueryCursor.prototype.next() no longer accepts a callback');
}
if (this._closed) {
throw new MongooseError('Cannot call `next()` on a closed cursor');
}
return new Promise((resolve, reject) => {
_next(this, function(error, doc) {
if (error) {
return reject(error);
}
resolve(doc);
});
});
};
/**
* Execute `fn` for every document in the cursor. If `fn` returns a promise,
* will wait for the promise to resolve before iterating on to the next one.
* Returns a promise that resolves when done.
*
* #### Example:
*
* // Iterate over documents asynchronously
* Thing.
* find({ name: /^hello/ }).
* cursor().
* eachAsync(async function (doc, i) {
* doc.foo = doc.bar + i;
* await doc.save();
* })
*
* @param {Function} fn
* @param {object} [options]
* @param {number} [options.parallel] the number of promises to execute in parallel. Defaults to 1.
* @param {number} [options.batchSize] if set, will call `fn()` with arrays of documents with length at most `batchSize`
* @param {boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error.
* @return {Promise}
* @api public
* @method eachAsync
*/
QueryCursor.prototype.eachAsync = function(fn, opts) {
if (typeof arguments[2] === 'function') {
throw new MongooseError('QueryCursor.prototype.eachAsync() no longer accepts a callback');
}
if (typeof opts === 'function') {
opts = {};
}
opts = opts || {};
return eachAsync((cb) => _next(this, cb), fn, opts);
};
/**
* The `options` passed in to the `QueryCursor` constructor.
*
* @api public
* @property options
*/
QueryCursor.prototype.options;
/**
* Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html#addCursorFlag).
* Useful for setting the `noCursorTimeout` and `tailable` flags.
*
* @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag
* @param {boolean} value
* @return {AggregationCursor} this
* @api public
* @method addCursorFlag
*/
QueryCursor.prototype.addCursorFlag = function(flag, value) {
_waitForCursor(this, () => {
this.cursor.addCursorFlag(flag, value);
});
return this;
};
/**
* Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js).
* You do not need to call this function explicitly, the JavaScript runtime
* will call it for you.
*
* #### Example:
*
* // Works without using `cursor()`
* for await (const doc of Model.find([{ $sort: { name: 1 } }])) {
* console.log(doc.name);
* }
*
* // Can also use `cursor()`
* for await (const doc of Model.find([{ $sort: { name: 1 } }]).cursor()) {
* console.log(doc.name);
* }
*
* Node.js 10.x supports async iterators natively without any flags. You can
* enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187).
*
* **Note:** This function is not if `Symbol.asyncIterator` is undefined. If
* `Symbol.asyncIterator` is undefined, that means your Node.js version does not
* support async iterators.
*
* @method [Symbol.asyncIterator]
* @memberOf QueryCursor
* @instance
* @api public
*/
if (Symbol.asyncIterator != null) {
QueryCursor.prototype[Symbol.asyncIterator] = function queryCursorAsyncIterator() {
// Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
this._mongooseOptions._asyncIterator = true;
return this;
};
}
/**
* Get the next doc from the underlying cursor and mongooseify it
* (populate, etc.)
* @param {any} ctx
* @param {Function} cb
* @api private
*/
function _next(ctx, cb) {
let callback = cb;
// Create a custom callback to handle transforms, async iterator, and transformNull
callback = function(err, doc) {
if (err) {
return cb(err);
}
// Handle null documents - if asyncIterator, we need to return `done: true`, otherwise just
// skip. In either case, avoid transforms.
if (doc === null) {
if (ctx._mongooseOptions._asyncIterator) {
return cb(null, { done: true });
} else {
return cb(null, null);
}
}
// Apply transforms
if (ctx._transforms.length && doc !== null) {
doc = ctx._transforms.reduce(function(doc, fn) {
return fn.call(ctx, doc);
}, doc);
}
// This option is set in `Symbol.asyncIterator` code paths.
// For async iterator, we need to convert to {value, done} format
if (ctx._mongooseOptions._asyncIterator) {
return cb(null, { value: doc, done: false });
}
return cb(null, doc);
};
if (ctx._error) {
return immediate(function() {
callback(ctx._error);
});
}
if (ctx.skipped) {
return immediate(() => callback(null, null));
}
if (ctx.cursor) {
if (ctx.query._mongooseOptions.populate && !ctx._pop) {
ctx._pop = helpers.preparePopulationOptionsMQ(ctx.query,
ctx.query._mongooseOptions);
ctx._pop.__noPromise = true;
}
if (ctx.query._mongooseOptions.populate && ctx.options._populateBatchSize > 1) {
if (ctx._batchDocs && ctx._batchDocs.length) {
// Return a cached populated doc
return _nextDoc(ctx, ctx._batchDocs.shift(), ctx._pop, callback);
} else if (ctx._batchExhausted) {
// Internal cursor reported no more docs. Act the same here
return callback(null, null);
} else {
// Request as many docs as batchSize, to populate them also in batch
ctx._batchDocs = [];
ctx.cursor.next().then(
res => { _onNext.call({ ctx, callback }, null, res); },
err => { _onNext.call({ ctx, callback }, err); }
);
return;
}
} else {
return ctx.cursor.next().then(
doc => {
if (!doc) {
callback(null, null);
return;
}
if (!ctx.query._mongooseOptions.populate) {
return _nextDoc(ctx, doc, null, callback);
}
_nextDoc(ctx, doc, ctx._pop, (err, doc) => {
if (err != null) {
return callback(err);
}
ctx.query.model.populate(doc, ctx._pop).then(
doc => callback(null, doc),
err => callback(err)
);
});
},
error => {
callback(error);
}
);
}
} else {
ctx.once('error', cb);
ctx.once('cursor', function(cursor) {
ctx.removeListener('error', cb);
if (cursor == null) {
if (ctx.skipped) {
return cb(null, null);
}
return;
}
_next(ctx, cb);
});
}
}
/*!
* ignore
*/
function _onNext(error, doc) {
if (error) {
return this.callback(error);
}
if (!doc) {
this.ctx._batchExhausted = true;
return _populateBatch.call(this);
}
this.ctx._batchDocs.push(doc);
if (this.ctx._batchDocs.length < this.ctx.options._populateBatchSize) {
// If both `batchSize` and `_populateBatchSize` are huge, calling `next()` repeatedly may
// cause a stack overflow. So make sure we clear the stack.
immediate(() => this.ctx.cursor.next().then(
res => { _onNext.call(this, null, res); },
err => { _onNext.call(this, err); }
));
} else {
_populateBatch.call(this);
}
}
/*!
* ignore
*/
function _populateBatch() {
if (!this.ctx._batchDocs.length) {
return this.callback(null, null);
}
this.ctx.query.model.populate(this.ctx._batchDocs, this.ctx._pop).then(
() => {
_nextDoc(this.ctx, this.ctx._batchDocs.shift(), this.ctx._pop, this.callback);
},
err => {
this.callback(err);
}
);
}
/*!
* ignore
*/
function _nextDoc(ctx, doc, pop, callback) {
if (ctx.query._mongooseOptions.lean) {
return ctx.model.hooks.execPost('find', ctx.query, [[doc]]).then(() => callback(null, doc), err => callback(err));
}
const { model, _fields, _userProvidedFields, options } = ctx.query;
helpers.createModelAndInit(model, doc, _fields, _userProvidedFields, options, pop, (err, doc) => {
if (err != null) {
return callback(err);
}
if (options.session != null) {
doc.$session(options.session);
}
ctx.model.hooks.execPost('find', ctx.query, [[doc]]).then(() => callback(null, doc), err => callback(err));
});
}
/*!
* ignore
*/
function _waitForCursor(ctx, cb) {
if (ctx.cursor) {
return cb();
}
ctx.once('cursor', function(cursor) {
if (cursor == null) {
return;
}
cb();
});
}
module.exports = QueryCursor;

5599
backend/node_modules/mongoose/lib/document.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

15
backend/node_modules/mongoose/lib/driver.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
/*!
* ignore
*/
let driver = null;
module.exports.get = function() {
return driver;
};
module.exports.set = function(v) {
driver = v;
};

4
backend/node_modules/mongoose/lib/drivers/SPEC.md generated vendored Normal file
View File

@@ -0,0 +1,4 @@
# Driver Spec
TODO

View File

@@ -0,0 +1,5 @@
'use strict';
const BulkWriteResult = require('mongodb/lib/bulk/common').BulkWriteResult;
module.exports = BulkWriteResult;

View File

@@ -0,0 +1,393 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseCollection = require('../../collection');
const MongooseError = require('../../error/mongooseError');
const Collection = require('mongodb').Collection;
const ObjectId = require('../../types/objectid');
const getConstructorName = require('../../helpers/getConstructorName');
const internalToObjectOptions = require('../../options').internalToObjectOptions;
const stream = require('stream');
const util = require('util');
const formatToObjectOptions = Object.freeze({ ...internalToObjectOptions, copyTrustedSymbol: false });
/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
*
* All methods methods from the [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver are copied and wrapped in queue management.
*
* @inherits Collection https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html
* @api private
*/
function NativeCollection(name, conn, options) {
this.collection = null;
this.Promise = options.Promise || Promise;
this.modelName = options.modelName;
delete options.modelName;
this._closed = false;
MongooseCollection.apply(this, arguments);
}
/*!
* Inherit from abstract Collection.
*/
Object.setPrototypeOf(NativeCollection.prototype, MongooseCollection.prototype);
/**
* Called when the connection opens.
*
* @api private
*/
NativeCollection.prototype.onOpen = function() {
this.collection = this.conn.db.collection(this.name);
MongooseCollection.prototype.onOpen.call(this);
return this.collection;
};
/**
* Called when the connection closes
*
* @api private
*/
NativeCollection.prototype.onClose = function(force) {
MongooseCollection.prototype.onClose.call(this, force);
};
/**
* Helper to get the collection, in case `this.collection` isn't set yet.
* May happen if `bufferCommands` is false and created the model when
* Mongoose was disconnected.
*
* @api private
*/
NativeCollection.prototype._getCollection = function _getCollection() {
if (this.collection) {
return this.collection;
}
if (this.conn.db != null) {
this.collection = this.conn.db.collection(this.name);
return this.collection;
}
return null;
};
/**
* Copy the collection methods and make them subject to queues
* @param {number|string} I
* @api private
*/
function iter(i) {
NativeCollection.prototype[i] = function() {
const collection = this._getCollection();
const args = Array.from(arguments);
const _this = this;
const globalDebug = _this?.conn?.base?.options?.debug;
const connectionDebug = _this?.conn?.options?.debug;
const debug = connectionDebug == null ? globalDebug : connectionDebug;
const opId = new ObjectId();
// If user force closed, queueing will hang forever. See #5664
if (this.conn.$wasForceClosed) {
const error = new MongooseError('Connection was force closed');
if (args.length > 0 &&
typeof args[args.length - 1] === 'function') {
args[args.length - 1](error);
return;
} else {
throw error;
}
}
let timeout = null;
let waitForBufferPromise = null;
if (this._shouldBufferCommands() && this.buffer) {
this.conn.emit('buffer', {
_id: opId,
modelName: _this.modelName,
collectionName: _this.name,
method: i,
args: args
});
const bufferTimeoutMS = this._getBufferTimeoutMS();
waitForBufferPromise = new Promise((resolve, reject) => {
this.addQueue(resolve);
timeout = setTimeout(() => {
const removed = this.removeQueue(resolve);
if (removed) {
const message = 'Operation `' + this.name + '.' + i + '()` buffering timed out after ' +
bufferTimeoutMS + 'ms';
const err = new MongooseError(message);
this.conn.emit('buffer-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err });
reject(err);
}
}, bufferTimeoutMS);
});
return waitForBufferPromise.then(() => {
if (timeout) {
clearTimeout(timeout);
}
return this[i].apply(this, args);
});
}
if (debug) {
if (typeof debug === 'function') {
let argsToAdd = null;
if (typeof args[args.length - 1] == 'function') {
argsToAdd = args.slice(0, args.length - 1);
} else {
argsToAdd = args;
}
debug.apply(_this,
[_this.name, i].concat(argsToAdd));
} else if (debug instanceof stream.Writable) {
this.$printToStream(_this.name, i, args, debug);
} else {
const color = debug.color == null ? true : debug.color;
const shell = debug.shell == null ? false : debug.shell;
this.$print(_this.name, i, args, color, shell);
}
}
this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: args });
try {
if (collection == null) {
const message = 'Cannot call `' + this.name + '.' + i + '()` before initial connection ' +
'is complete if `bufferCommands = false`. Make sure you `await mongoose.connect()` if ' +
'you have `bufferCommands = false`.';
throw new MongooseError(message);
}
const ret = collection[i].apply(collection, args);
if (typeof ret?.then === 'function') {
return ret.then(
result => {
if (timeout != null) {
clearTimeout(timeout);
}
this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result });
return result;
},
error => {
if (timeout != null) {
clearTimeout(timeout);
}
this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error });
throw error;
}
);
}
this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result: ret });
if (timeout != null) {
clearTimeout(timeout);
}
return ret;
} catch (error) {
if (timeout != null) {
clearTimeout(timeout);
}
this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error });
throw error;
}
};
}
for (const key of Object.getOwnPropertyNames(Collection.prototype)) {
// Janky hack to work around gh-3005 until we can get rid of the mongoose
// collection abstraction
const descriptor = Object.getOwnPropertyDescriptor(Collection.prototype, key);
// Skip properties with getters because they may throw errors (gh-8528)
if (descriptor.get !== undefined) {
continue;
}
if (typeof Collection.prototype[key] !== 'function') {
continue;
}
iter(key);
}
/**
* Debug print helper
*
* @api public
* @method $print
*/
NativeCollection.prototype.$print = function(name, i, args, color, shell) {
const moduleName = color ? '\x1B[0;36mMongoose:\x1B[0m ' : 'Mongoose: ';
const functionCall = [name, i].join('.');
const _args = [];
for (let j = args.length - 1; j >= 0; --j) {
if (this.$format(args[j]) || _args.length) {
_args.unshift(this.$format(args[j], color, shell));
}
}
const params = '(' + _args.join(', ') + ')';
console.info(moduleName + functionCall + params);
};
/**
* Debug print helper
*
* @api public
* @method $print
*/
NativeCollection.prototype.$printToStream = function(name, i, args, stream) {
const functionCall = [name, i].join('.');
const _args = [];
for (let j = args.length - 1; j >= 0; --j) {
if (this.$format(args[j]) || _args.length) {
_args.unshift(this.$format(args[j]));
}
}
const params = '(' + _args.join(', ') + ')';
stream.write(functionCall + params, 'utf8');
};
/**
* Formatter for debug print args
*
* @api public
* @method $format
*/
NativeCollection.prototype.$format = function(arg, color, shell) {
const type = typeof arg;
if (type === 'function' || type === 'undefined') return '';
return format(arg, false, color, shell);
};
/**
* Debug print helper
* @param {any} representation
* @api private
*/
function inspectable(representation) {
const ret = {
inspect: function() { return representation; }
};
if (util.inspect.custom) {
ret[util.inspect.custom] = ret.inspect;
}
return ret;
}
function map(o) {
return format(o, true);
}
function formatObjectId(x, key) {
x[key] = inspectable('ObjectId("' + x[key].toHexString() + '")');
}
function formatDate(x, key, shell) {
if (shell) {
x[key] = inspectable('ISODate("' + x[key].toUTCString() + '")');
} else {
x[key] = inspectable('new Date("' + x[key].toUTCString() + '")');
}
}
function format(obj, sub, color, shell) {
if (typeof obj?.toBSON === 'function') {
obj = obj.toBSON();
}
if (obj == null) {
return obj;
}
const clone = require('../../helpers/clone');
// `sub` indicates `format()` was called recursively, so skip cloning because we already
// did a deep clone on the top-level object.
let x = sub ? obj : clone(obj, formatToObjectOptions);
const constructorName = getConstructorName(x);
if (constructorName === 'Binary') {
x = 'BinData(' + x.sub_type + ', "' + x.toString('base64') + '")';
} else if (constructorName === 'ObjectId') {
x = inspectable('ObjectId("' + x.toHexString() + '")');
} else if (constructorName === 'Date') {
x = inspectable('new Date("' + x.toUTCString() + '")');
} else if (constructorName === 'Object') {
const keys = Object.keys(x);
const numKeys = keys.length;
let key;
for (let i = 0; i < numKeys; ++i) {
key = keys[i];
if (x[key]) {
let error;
if (typeof x[key].toBSON === 'function') {
try {
// `session.toBSON()` throws an error. This means we throw errors
// in debug mode when using transactions, see gh-6712. As a
// workaround, catch `toBSON()` errors, try to serialize without
// `toBSON()`, and rethrow if serialization still fails.
x[key] = x[key].toBSON();
} catch (_error) {
error = _error;
}
}
const _constructorName = getConstructorName(x[key]);
if (_constructorName === 'Binary') {
x[key] = 'BinData(' + x[key].sub_type + ', "' +
x[key].buffer.toString('base64') + '")';
} else if (_constructorName === 'Object') {
x[key] = format(x[key], true);
} else if (_constructorName === 'ObjectId') {
formatObjectId(x, key);
} else if (_constructorName === 'Date') {
formatDate(x, key, shell);
} else if (_constructorName === 'ClientSession') {
x[key] = inspectable('ClientSession("' +
(x[key]?.id?.id?.buffer || '').toString('hex') + '")');
} else if (Array.isArray(x[key])) {
x[key] = x[key].map(map);
} else if (error != null) {
// If there was an error with `toBSON()` and the object wasn't
// already converted to a string representation, rethrow it.
// Open to better ideas on how to handle this.
throw error;
}
}
}
}
if (sub) {
return x;
}
return util.
inspect(x, false, 10, color).
replace(/\n/g, '').
replace(/\s{2,}/g, ' ');
}
/**
* Retrieves information about this collections indexes.
*
* @method getIndexes
* @api public
*/
NativeCollection.prototype.getIndexes = NativeCollection.prototype.indexInformation;
/*!
* Module exports.
*/
module.exports = NativeCollection;

View File

@@ -0,0 +1,506 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseConnection = require('../../connection');
const MongooseError = require('../../error/index');
const STATES = require('../../connectionState');
const mongodb = require('mongodb');
const pkg = require('../../../package.json');
const processConnectionOptions = require('../../helpers/processConnectionOptions');
const setTimeout = require('../../helpers/timers').setTimeout;
const utils = require('../../utils');
const Schema = require('../../schema');
/**
* A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation.
*
* @inherits Connection
* @api private
*/
function NativeConnection() {
MongooseConnection.apply(this, arguments);
this._listening = false;
// Tracks the last time (as unix timestamp) the connection received a
// serverHeartbeatSucceeded or serverHeartbeatFailed event from the underlying MongoClient.
// If we haven't received one in a while (like due to a frozen AWS Lambda container) then
// `readyState` is likely stale.
this._lastHeartbeatAt = null;
}
/**
* Expose the possible connection states.
* @api public
*/
NativeConnection.STATES = STATES;
/*!
* Inherits from Connection.
*/
Object.setPrototypeOf(NativeConnection.prototype, MongooseConnection.prototype);
/**
* Switches to a different database using the same connection pool.
*
* Returns a new connection object, with the new db. If you set the `useCache`
* option, `useDb()` will cache connections by `name`.
*
* **Note:** Calling `close()` on a `useDb()` connection will close the base connection as well.
*
* @param {string} name The database name
* @param {object} [options]
* @param {boolean} [options.useCache=false] If true, cache results so calling `useDb()` multiple times with the same name only creates 1 connection object.
* @return {Connection} New Connection Object
* @api public
*/
NativeConnection.prototype.useDb = function(name, options) {
// Return immediately if cached
options = options || {};
if (options.useCache && this.relatedDbs[name]) {
return this.relatedDbs[name];
}
// we have to manually copy all of the attributes...
const newConn = new this.constructor();
newConn.name = name;
newConn.base = this.base;
newConn.collections = {};
newConn.models = {};
newConn.replica = this.replica;
newConn.config = Object.assign({}, this.config, newConn.config);
newConn.name = this.name;
newConn.options = this.options;
newConn._readyState = this._readyState;
newConn._closeCalled = this._closeCalled;
newConn._hasOpened = this._hasOpened;
newConn._listening = false;
newConn._parent = this;
newConn.host = this.host;
newConn.port = this.port;
newConn.user = this.user;
newConn.pass = this.pass;
// First, when we create another db object, we are not guaranteed to have a
// db object to work with. So, in the case where we have a db object and it
// is connected, we can just proceed with setting everything up. However, if
// we do not have a db or the state is not connected, then we need to wait on
// the 'open' event of the connection before doing the rest of the setup
// the 'connected' event is the first time we'll have access to the db object
const _this = this;
newConn.client = _this.client;
if (this.db && this._readyState === STATES.connected) {
wireup();
} else {
this._queue.push({ fn: wireup });
}
function wireup() {
newConn.client = _this.client;
newConn.db = _this.client.db(name);
newConn._lastHeartbeatAt = _this._lastHeartbeatAt;
newConn.onOpen();
}
newConn.name = name;
// push onto the otherDbs stack, this is used when state changes
this.otherDbs.push(newConn);
newConn.otherDbs.push(this);
// push onto the relatedDbs cache, this is used when state changes
if (options?.useCache) {
this.relatedDbs[newConn.name] = newConn;
newConn.relatedDbs = this.relatedDbs;
}
return newConn;
};
/**
* Runs a [db-level aggregate()](https://www.mongodb.com/docs/manual/reference/method/db.aggregate/) on this connection's underlying `db`
*
* @param {Array} pipeline
* @param {object} [options]
*/
NativeConnection.prototype.aggregate = function aggregate(pipeline, options) {
return new this.base.Aggregate(null, this).append(pipeline).option(options ?? {});
};
/**
* Removes the database connection with the given name created with `useDb()`.
*
* Throws an error if the database connection was not found.
*
* #### Example:
*
* // Connect to `initialdb` first
* const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/initialdb').asPromise();
*
* // Creates an un-cached connection to `mydb`
* const db = conn.useDb('mydb');
*
* // Closes `db`, and removes `db` from `conn.relatedDbs` and `conn.otherDbs`
* await conn.removeDb('mydb');
*
* @method removeDb
* @memberOf Connection
* @param {string} name The database name
* @return {Connection} this
*/
NativeConnection.prototype.removeDb = function removeDb(name) {
const dbs = this.otherDbs.filter(db => db.name === name);
if (!dbs.length) {
throw new MongooseError(`No connections to database "${name}" found`);
}
for (const db of dbs) {
db._closeCalled = true;
db._destroyCalled = true;
db._readyState = STATES.disconnected;
db.$wasForceClosed = true;
}
delete this.relatedDbs[name];
this.otherDbs = this.otherDbs.filter(db => db.name !== name);
};
/**
* Closes the connection
*
* @param {boolean} [force]
* @return {Connection} this
* @api private
*/
NativeConnection.prototype.doClose = async function doClose(force) {
if (this.client == null) {
return this;
}
let skipCloseClient = false;
if (force != null && typeof force === 'object') {
skipCloseClient = force.skipCloseClient;
force = force.force;
}
if (skipCloseClient) {
return this;
}
await this.client.close(force);
// Defer because the driver will wait at least 1ms before finishing closing
// the pool, see https://github.com/mongodb-js/mongodb-core/blob/a8f8e4ce41936babc3b9112bf42d609779f03b39/lib/connection/pool.js#L1026-L1030.
// If there's queued operations, you may still get some background work
// after the callback is called.
await new Promise(resolve => setTimeout(resolve, 1));
return this;
};
/**
* Implementation of `listDatabases()` for MongoDB driver
*
* @return {Promise}
* @api public
*/
NativeConnection.prototype.listDatabases = async function listDatabases() {
await this._waitForConnect();
return await this.db.admin().listDatabases();
};
/*!
* ignore
*/
NativeConnection.prototype.createClient = async function createClient(uri, options) {
if (typeof uri !== 'string') {
throw new MongooseError('The `uri` parameter to `openUri()` must be a ' +
`string, got "${typeof uri}". Make sure the first parameter to ` +
'`mongoose.connect()` or `mongoose.createConnection()` is a string.');
}
if (this._destroyCalled) {
throw new MongooseError(
'Connection has been closed and destroyed, and cannot be used for re-opening the connection. ' +
'Please create a new connection with `mongoose.createConnection()` or `mongoose.connect()`.'
);
}
if (this.readyState === STATES.connecting || this.readyState === STATES.connected) {
if (this._connectionString !== uri) {
throw new MongooseError('Can\'t call `openUri()` on an active connection with ' +
'different connection strings. Make sure you aren\'t calling `mongoose.connect()` ' +
'multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections');
}
}
options = processConnectionOptions(uri, options);
if (options) {
const autoIndex = options.config?.autoIndex ?? options.autoIndex;
if (autoIndex != null) {
this.config.autoIndex = autoIndex !== false;
delete options.config;
delete options.autoIndex;
}
if ('autoCreate' in options) {
this.config.autoCreate = !!options.autoCreate;
delete options.autoCreate;
}
if ('sanitizeFilter' in options) {
this.config.sanitizeFilter = options.sanitizeFilter;
delete options.sanitizeFilter;
}
if ('autoSearchIndex' in options) {
this.config.autoSearchIndex = options.autoSearchIndex;
delete options.autoSearchIndex;
}
if ('bufferTimeoutMS' in options) {
this.config.bufferTimeoutMS = options.bufferTimeoutMS;
delete options.bufferTimeoutMS;
}
// Backwards compat
if (options.user || options.pass) {
options.auth = options.auth || {};
options.auth.username = options.user;
options.auth.password = options.pass;
this.user = options.user;
this.pass = options.pass;
}
delete options.user;
delete options.pass;
if (options.bufferCommands != null) {
this.config.bufferCommands = options.bufferCommands;
delete options.bufferCommands;
}
} else {
options = {};
}
this._connectionOptions = options;
const dbName = options.dbName;
if (dbName != null) {
this.$dbName = dbName;
}
delete options.dbName;
if (!utils.hasUserDefinedProperty(options, 'driverInfo')) {
options.driverInfo = {
name: 'Mongoose',
version: pkg.version
};
}
const { schemaMap, encryptedFieldsMap } = this._buildEncryptionSchemas();
if ((utils.hasOwnKeys(schemaMap) || utils.hasOwnKeys(encryptedFieldsMap)) && !options.autoEncryption) {
throw new Error('Must provide `autoEncryption` when connecting with encrypted schemas.');
}
if (utils.hasOwnKeys(schemaMap)) {
options.autoEncryption.schemaMap = schemaMap;
}
if (utils.hasOwnKeys(encryptedFieldsMap)) {
options.autoEncryption.encryptedFieldsMap = encryptedFieldsMap;
}
this.readyState = STATES.connecting;
this._connectionString = uri;
let client;
try {
client = new mongodb.MongoClient(uri, options);
} catch (error) {
this.readyState = STATES.disconnected;
throw error;
}
this.client = client;
client.setMaxListeners(0);
await client.connect();
_setClient(this, client, options, dbName);
for (const db of this.otherDbs) {
_setClient(db, client, {}, db.name);
}
return this;
};
/**
* Given a connection, which may or may not have encrypted models, build
* a schemaMap and/or an encryptedFieldsMap for the connection, combining all models
* into a single schemaMap and encryptedFields map.
*
* @returns {object} the generated schemaMap and encryptedFieldsMap
*/
NativeConnection.prototype._buildEncryptionSchemas = function() {
const qeMappings = {};
const csfleMappings = {};
const encryptedModels = Object.values(this.models).filter(model => model.schema._hasEncryptedFields());
// If discriminators are configured for the collection, there might be multiple models
// pointing to the same namespace. For this scenario, we merge all the schemas for each namespace
// into a single schema and then generate a schemaMap/encryptedFieldsMap for the combined schema.
for (const model of encryptedModels) {
const { schema, collection: { collectionName } } = model;
const namespace = `${this.$dbName}.${collectionName}`;
const mappings = schema.encryptionType() === 'csfle' ? csfleMappings : qeMappings;
mappings[namespace] ??= new Schema({}, { encryptionType: schema.encryptionType() });
const isNonRootDiscriminator = schema.discriminatorMapping && !schema.discriminatorMapping.isRoot;
if (isNonRootDiscriminator) {
const rootSchema = schema._baseSchema;
schema.eachPath((pathname) => {
if (rootSchema.path(pathname)) return;
if (!mappings[namespace]._hasEncryptedField(pathname)) return;
throw new Error(`Cannot have duplicate keys in discriminators with encryption. key=${pathname}`);
});
}
mappings[namespace].add(schema);
}
const schemaMap = Object.fromEntries(Object.entries(csfleMappings).map(
([namespace, schema]) => ([namespace, schema._buildSchemaMap()])
));
const encryptedFieldsMap = Object.fromEntries(Object.entries(qeMappings).map(
([namespace, schema]) => ([namespace, schema._buildEncryptedFields()])
));
return {
schemaMap, encryptedFieldsMap
};
};
/*!
* ignore
*/
NativeConnection.prototype.setClient = function setClient(client) {
if (!(client instanceof mongodb.MongoClient)) {
throw new MongooseError('Must call `setClient()` with an instance of MongoClient');
}
if (this.readyState !== STATES.disconnected) {
throw new MongooseError('Cannot call `setClient()` on a connection that is already connected.');
}
if (client.topology == null) {
throw new MongooseError('Cannot call `setClient()` with a MongoClient that you have not called `connect()` on yet.');
}
this._connectionString = client.s.url;
_setClient(this, client, {}, client.s.options.dbName);
for (const model of Object.values(this.models)) {
// Errors handled internally, so safe to ignore error
model.init().catch(function $modelInitNoop() {});
}
return this;
};
/*!
* ignore
*/
function _setClient(conn, client, options, dbName) {
const db = dbName != null ? client.db(dbName) : client.db();
conn.db = db;
conn.client = client;
conn.host = client?.s?.options?.hosts?.[0]?.host;
conn.port = client?.s?.options?.hosts?.[0]?.port;
conn.name = dbName != null ? dbName : db.databaseName;
conn._closeCalled = client._closeCalled;
const _handleReconnect = () => {
// If we aren't disconnected, we assume this reconnect is due to a
// socket timeout. If there's no activity on a socket for
// `socketTimeoutMS`, the driver will attempt to reconnect and emit
// this event.
if (conn.readyState !== STATES.connected) {
conn.readyState = STATES.connected;
conn.emit('reconnect');
conn.emit('reconnected');
conn.onOpen();
}
};
const type = client?.topology?.description?.type || '';
if (type === 'Single') {
client.on('serverDescriptionChanged', ev => {
const newDescription = ev.newDescription;
if (newDescription.type === 'Unknown') {
conn.readyState = STATES.disconnected;
} else {
_handleReconnect();
}
});
} else if (type.startsWith('ReplicaSet')) {
client.on('topologyDescriptionChanged', ev => {
// Emit disconnected if we've lost connectivity to the primary
const description = ev.newDescription;
if (conn.readyState === STATES.connected && description.type !== 'ReplicaSetWithPrimary') {
// Implicitly emits 'disconnected'
conn.readyState = STATES.disconnected;
} else if (conn.readyState === STATES.disconnected && description.type === 'ReplicaSetWithPrimary') {
_handleReconnect();
}
});
}
conn._lastHeartbeatAt = null;
client.on('serverHeartbeatSucceeded', () => {
conn._lastHeartbeatAt = Date.now();
for (const otherDb of conn.otherDbs) {
otherDb._lastHeartbeatAt = conn._lastHeartbeatAt;
}
});
if (options.monitorCommands) {
client.on('commandStarted', (data) => conn.emit('commandStarted', data));
client.on('commandFailed', (data) => conn.emit('commandFailed', data));
client.on('commandSucceeded', (data) => conn.emit('commandSucceeded', data));
}
conn.onOpen();
for (const i in conn.collections) {
if (Object.hasOwn(conn.collections, i)) {
conn.collections[i].onOpen();
}
}
}
/*!
* Module exports.
*/
module.exports = NativeConnection;

View File

@@ -0,0 +1,10 @@
/*!
* Module exports.
*/
'use strict';
exports.BulkWriteResult = require('./bulkWriteResult');
exports.Collection = require('./collection');
exports.Connection = require('./connection');
exports.ClientEncryption = require('mongodb').ClientEncryption;

View File

@@ -0,0 +1,29 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* MissingSchema Error constructor.
*/
class MissingSchemaError extends MongooseError {
constructor() {
super('Schema hasn\'t been registered for document.\n'
+ 'Use mongoose.Document(name, schema)');
}
}
Object.defineProperty(MissingSchemaError.prototype, 'name', {
value: 'MongooseError'
});
/*!
* exports
*/
module.exports = MissingSchemaError;

View File

@@ -0,0 +1,44 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* If the underwriting `bulkWrite()` for `bulkSave()` succeeded, but wasn't able to update or
* insert all documents, we throw this error.
*
* @api private
*/
class MongooseBulkSaveIncompleteError extends MongooseError {
constructor(modelName, documents, bulkWriteResult) {
const matchedCount = bulkWriteResult?.matchedCount ?? 0;
const insertedCount = bulkWriteResult?.insertedCount ?? 0;
let preview = documents.map(doc => doc._id).join(', ');
if (preview.length > 100) {
preview = preview.slice(0, 100) + '...';
}
const numDocumentsNotUpdated = documents.length - matchedCount - insertedCount;
super(`${modelName}.bulkSave() was not able to update ${numDocumentsNotUpdated} of the given documents due to incorrect version or optimistic concurrency, document ids: ${preview}`);
this.modelName = modelName;
this.documents = documents;
this.bulkWriteResult = bulkWriteResult;
this.numDocumentsNotUpdated = numDocumentsNotUpdated;
}
}
Object.defineProperty(MongooseBulkSaveIncompleteError.prototype, 'name', {
value: 'MongooseBulkSaveIncompleteError'
});
/*!
* exports
*/
module.exports = MongooseBulkSaveIncompleteError;

View File

@@ -0,0 +1,41 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./');
/**
* If `bulkWrite()` or `insertMany()` has validation errors, but
* all valid operations succeed, and 'throwOnValidationError' is true,
* Mongoose will throw this error.
*
* @api private
*/
class MongooseBulkWriteError extends MongooseError {
constructor(validationErrors, results, rawResult, operation) {
let preview = validationErrors.map(e => e.message).join(', ');
if (preview.length > 200) {
preview = preview.slice(0, 200) + '...';
}
super(`${operation} failed with ${validationErrors.length} Mongoose validation errors: ${preview}`);
this.validationErrors = validationErrors;
this.results = results;
this.rawResult = rawResult;
this.operation = operation;
}
}
Object.defineProperty(MongooseBulkWriteError.prototype, 'name', {
value: 'MongooseBulkWriteError'
});
/*!
* exports
*/
module.exports = MongooseBulkWriteError;

158
backend/node_modules/mongoose/lib/error/cast.js generated vendored Normal file
View File

@@ -0,0 +1,158 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
const util = require('util');
/**
* Casting Error constructor.
*
* @param {string} type
* @param {string} value
* @inherits MongooseError
* @api private
*/
class CastError extends MongooseError {
constructor(type, value, path, reason, schemaType) {
// If no args, assume we'll `init()` later.
if (arguments.length > 0) {
const valueType = getValueType(value);
const messageFormat = getMessageFormat(schemaType);
const msg = formatMessage(null, type, value, path, messageFormat, valueType, reason);
super(msg);
this.init(type, value, path, reason, schemaType);
} else {
super(formatMessage());
}
}
toJSON() {
return {
stringValue: this.stringValue,
valueType: this.valueType,
kind: this.kind,
value: this.value,
path: this.path,
reason: this.reason,
name: this.name,
message: this.message
};
}
/*!
* ignore
*/
init(type, value, path, reason, schemaType) {
this.stringValue = getStringValue(value);
this.messageFormat = getMessageFormat(schemaType);
this.kind = type;
this.value = value;
this.path = path;
this.reason = reason;
this.valueType = getValueType(value);
}
/**
* ignore
* @param {Readonly<CastError>} other
* @api private
*/
copy(other) {
this.messageFormat = other.messageFormat;
this.stringValue = other.stringValue;
this.kind = other.kind;
this.value = other.value;
this.path = other.path;
this.reason = other.reason;
this.message = other.message;
this.valueType = other.valueType;
}
/*!
* ignore
*/
setModel(model) {
this.message = formatMessage(model, this.kind, this.value, this.path,
this.messageFormat, this.valueType);
}
}
Object.defineProperty(CastError.prototype, 'name', {
value: 'CastError'
});
function getStringValue(value) {
let stringValue = util.inspect(value);
stringValue = stringValue.replace(/^'|'$/g, '"');
if (!stringValue.startsWith('"')) {
stringValue = '"' + stringValue + '"';
}
return stringValue;
}
function getValueType(value) {
if (value == null) {
return '' + value;
}
const t = typeof value;
if (t !== 'object') {
return t;
}
if (typeof value.constructor !== 'function') {
return t;
}
return value.constructor.name;
}
function getMessageFormat(schemaType) {
const messageFormat = schemaType?._castErrorMessage || null;
if (typeof messageFormat === 'string' || typeof messageFormat === 'function') {
return messageFormat;
}
}
/*!
* ignore
*/
function formatMessage(model, kind, value, path, messageFormat, valueType, reason) {
if (typeof messageFormat === 'string') {
const stringValue = getStringValue(value);
let ret = messageFormat.
replace('{KIND}', kind).
replace('{VALUE}', stringValue).
replace('{PATH}', path);
if (model != null) {
ret = ret.replace('{MODEL}', model.modelName);
}
return ret;
} else if (typeof messageFormat === 'function') {
return messageFormat(value, path, model, kind);
} else {
const stringValue = getStringValue(value);
const valueTypeMsg = valueType ? ' (type ' + valueType + ')' : '';
let ret = 'Cast to ' + kind + ' failed for value ' +
stringValue + valueTypeMsg + ' at path "' + path + '"';
if (model != null) {
ret += ' for model "' + model.modelName + '"';
}
if (reason != null &&
typeof reason.constructor === 'function' &&
reason.constructor.name !== 'AssertionError' &&
reason.constructor.name !== 'Error') {
ret += ' because of "' + reason.constructor.name + '"';
}
return ret;
}
}
/*!
* exports
*/
module.exports = CastError;

View File

@@ -0,0 +1,26 @@
'use strict';
const MongooseError = require('./mongooseError');
/**
* createCollections Error constructor
*
* @param {string} message
* @param {string} errorsMap
* @inherits MongooseError
* @api private
*/
class CreateCollectionsError extends MongooseError {
constructor(message, errorsMap) {
super(message);
this.errors = errorsMap;
}
}
Object.defineProperty(CreateCollectionsError.prototype, 'name', {
value: 'CreateCollectionsError'
});
module.exports = CreateCollectionsError;

View File

@@ -0,0 +1,40 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* DivergentArrayError constructor.
* @param {string[]} paths
* @api private
*/
class DivergentArrayError extends MongooseError {
constructor(paths) {
const msg = 'For your own good, using `document.save()` to update an array '
+ 'which was selected using an $elemMatch projection OR '
+ 'populated using skip, limit, query conditions, or exclusion of '
+ 'the _id field when the operation results in a $pop or $set of '
+ 'the entire array is not supported. The following '
+ 'path(s) would have been modified unsafely:\n'
+ ' ' + paths.join('\n ') + '\n'
+ 'Use Model.updateOne() to update these arrays instead. '
+ 'See https://mongoosejs.com/docs/faq.html#divergent-array-error for more information.';
super(msg);
}
}
Object.defineProperty(DivergentArrayError.prototype, 'name', {
value: 'DivergentArrayError'
});
/*!
* exports
*/
module.exports = DivergentArrayError;

View File

@@ -0,0 +1,41 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* If `eachAsync()` is called with `continueOnError: true`, there can be
* multiple errors. This error class contains an `errors` property, which
* contains an array of all errors that occurred in `eachAsync()`.
*
* @api private
*/
class EachAsyncMultiError extends MongooseError {
/**
* @param {string} connectionString
*/
constructor(errors) {
let preview = errors.map(e => e.message).join(', ');
if (preview.length > 50) {
preview = preview.slice(0, 50) + '...';
}
super(`eachAsync() finished with ${errors.length} errors: ${preview}`);
this.errors = errors;
}
}
Object.defineProperty(EachAsyncMultiError.prototype, 'name', {
value: 'EachAsyncMultiError'
});
/*!
* exports
*/
module.exports = EachAsyncMultiError;

237
backend/node_modules/mongoose/lib/error/index.js generated vendored Normal file
View File

@@ -0,0 +1,237 @@
'use strict';
/**
* MongooseError constructor. MongooseError is the base class for all
* Mongoose-specific errors.
*
* #### Example:
*
* const Model = mongoose.model('Test', new mongoose.Schema({ answer: Number }));
* const doc = new Model({ answer: 'not a number' });
* const err = doc.validateSync();
*
* err instanceof mongoose.Error.ValidationError; // true
*
* @constructor Error
* @param {string} msg Error message
* @inherits Error https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error
*/
const MongooseError = require('./mongooseError');
/**
* The name of the error. The name uniquely identifies this Mongoose error. The
* possible values are:
*
* - `MongooseError`: general Mongoose error
* - `CastError`: Mongoose could not convert a value to the type defined in the schema path. May be in a `ValidationError` class' `errors` property.
* - `DivergentArrayError`: You attempted to `save()` an array that was modified after you loaded it with a `$elemMatch` or similar projection
* - `MissingSchemaError`: You tried to access a model with [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.model()) that was not defined
* - `DocumentNotFoundError`: The document you tried to [`save()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.save()) was not found
* - `ValidatorError`: error from an individual schema path's validator
* - `ValidationError`: error returned from [`validate()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) or [`validateSync()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validateSync()). Contains zero or more `ValidatorError` instances in `.errors` property.
* - `MissingSchemaError`: You called `mongoose.Document()` without a schema
* - `ObjectExpectedError`: Thrown when you set a nested path to a non-object value with [strict mode set](https://mongoosejs.com/docs/guide.html#strict).
* - `ObjectParameterError`: Thrown when you pass a non-object value to a function which expects an object as a paramter
* - `OverwriteModelError`: Thrown when you call [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.model()) to re-define a model that was already defined.
* - `ParallelSaveError`: Thrown when you call [`save()`](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) on a document when the same document instance is already saving.
* - `StrictModeError`: Thrown when you set a path that isn't the schema and [strict mode](https://mongoosejs.com/docs/guide.html#strict) is set to `throw`.
* - `VersionError`: Thrown when the [document is out of sync](https://mongoosejs.com/docs/guide.html#versionKey)
*
* @api public
* @property {string} name
* @memberOf Error
* @instance
*/
/*!
* Module exports.
*/
module.exports = exports = MongooseError;
/**
* The default built-in validator error messages.
*
* @see Error.messages https://mongoosejs.com/docs/api/error.html#Error.messages
* @api public
* @memberOf Error
* @static
*/
MongooseError.messages = require('./messages');
// backward compat
MongooseError.Messages = MongooseError.messages;
/**
* An instance of this error class will be thrown when mongoose failed to
* cast a value.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.CastError = require('./cast');
/**
* An instance of this error class will be thrown when `save()` fails
* because the underlying
* document was not found. The constructor takes one parameter, the
* conditions that mongoose passed to `updateOne()` when trying to update
* the document.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.DocumentNotFoundError = require('./notFound');
/**
* An instance of this error class will be thrown when [validation](https://mongoosejs.com/docs/validation.html) failed.
* The `errors` property contains an object whose keys are the paths that failed and whose values are
* instances of CastError or ValidationError.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.ValidationError = require('./validation');
/**
* A `ValidationError` has a hash of `errors` that contain individual
* `ValidatorError` instances.
*
* #### Example:
*
* const schema = Schema({ name: { type: String, required: true } });
* const Model = mongoose.model('Test', schema);
* const doc = new Model({});
*
* // Top-level error is a ValidationError, **not** a ValidatorError
* const err = doc.validateSync();
* err instanceof mongoose.Error.ValidationError; // true
*
* // A ValidationError `err` has 0 or more ValidatorErrors keyed by the
* // path in the `err.errors` property.
* err.errors['name'] instanceof mongoose.Error.ValidatorError;
*
* err.errors['name'].kind; // 'required'
* err.errors['name'].path; // 'name'
* err.errors['name'].value; // undefined
*
* Instances of `ValidatorError` have the following properties:
*
* - `kind`: The validator's `type`, like `'required'` or `'regexp'`
* - `path`: The path that failed validation
* - `value`: The value that failed validation
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.ValidatorError = require('./validator');
/**
* An instance of this error class will be thrown when you call `save()` after
* the document in the database was changed in a potentially unsafe way. See
* the [`versionKey` option](https://mongoosejs.com/docs/guide.html#versionKey) for more information.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.VersionError = require('./version');
/**
* An instance of this error class will be thrown when you call `save()` multiple
* times on the same document in parallel. See the [FAQ](https://mongoosejs.com/docs/faq.html) for more
* information.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.ParallelSaveError = require('./parallelSave');
/**
* Thrown when a model with the given name was already registered on the connection.
* See [the FAQ about `OverwriteModelError`](https://mongoosejs.com/docs/faq.html#overwrite-model-error).
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.OverwriteModelError = require('./overwriteModel');
/**
* Thrown when you try to access a model that has not been registered yet
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.MissingSchemaError = require('./missingSchema');
/**
* Thrown when some documents failed to save when calling `bulkSave()`
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.MongooseBulkSaveIncompleteError = require('./bulkSaveIncompleteError');
/**
* Thrown when the MongoDB Node driver can't connect to a valid server
* to send an operation to.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.MongooseServerSelectionError = require('./serverSelection');
/**
* An instance of this error will be thrown if you used an array projection
* and then modified the array in an unsafe way.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.DivergentArrayError = require('./divergentArray');
/**
* Thrown when your try to pass values to model constructor that
* were not specified in schema or change immutable properties when
* `strict` mode is `"throw"`
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.StrictModeError = require('./strict');
/**
* An instance of this error class will be returned when mongoose failed to
* populate with a path that is not existing.
*
* @api public
* @memberOf Error
* @static
*/
MongooseError.StrictPopulateError = require('./strictPopulate');

View File

@@ -0,0 +1,32 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* InvalidSchemaOption Error constructor.
* @param {string} name
* @api private
*/
class InvalidSchemaOptionError extends MongooseError {
constructor(name, option) {
const msg = `Cannot create use schema for property "${name}" because the schema has the ${option} option enabled.`;
super(msg);
}
}
Object.defineProperty(InvalidSchemaOptionError.prototype, 'name', {
value: 'InvalidSchemaOptionError'
});
/*!
* exports
*/
module.exports = InvalidSchemaOptionError;

47
backend/node_modules/mongoose/lib/error/messages.js generated vendored Normal file
View File

@@ -0,0 +1,47 @@
/**
* The default built-in validator error messages. These may be customized.
*
* // customize within each schema or globally like so
* const mongoose = require('mongoose');
* mongoose.Error.messages.String.enum = "Your custom message for {PATH}.";
*
* Error messages support basic templating. Mongoose will replace the following strings with the corresponding value.
*
* - `{PATH}` is replaced with the invalid document path
* - `{VALUE}` is replaced with the invalid value
* - `{TYPE}` is replaced with the validator type such as "regexp", "min", or "user defined"
* - `{MIN}` is replaced with the declared min value for the Number.min validator
* - `{MAX}` is replaced with the declared max value for the Number.max validator
*
* Click the "show code" link below to see all defaults.
*
* @static
* @memberOf MongooseError
* @api public
*/
'use strict';
const msg = module.exports = exports = {};
msg.DocumentNotFoundError = null;
msg.general = {};
msg.general.default = 'Validator failed for path `{PATH}` with value `{VALUE}`';
msg.general.required = 'Path `{PATH}` is required.';
msg.Number = {};
msg.Number.min = 'Path `{PATH}` ({VALUE}) is less than minimum allowed value ({MIN}).';
msg.Number.max = 'Path `{PATH}` ({VALUE}) is more than maximum allowed value ({MAX}).';
msg.Number.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.';
msg.Date = {};
msg.Date.min = 'Path `{PATH}` ({VALUE}) is before minimum allowed value ({MIN}).';
msg.Date.max = 'Path `{PATH}` ({VALUE}) is after maximum allowed value ({MAX}).';
msg.String = {};
msg.String.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.';
msg.String.match = 'Path `{PATH}` is invalid ({VALUE}).';
msg.String.minlength = 'Path `{PATH}` (`{VALUE}`, length {LENGTH}) is shorter than the minimum allowed length ({MINLENGTH}).';
msg.String.maxlength = 'Path `{PATH}` (`{VALUE}`, length {LENGTH}) is longer than the maximum allowed length ({MAXLENGTH}).';

View File

@@ -0,0 +1,33 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* MissingSchema Error constructor.
* @param {string} name
* @api private
*/
class MissingSchemaError extends MongooseError {
constructor(name) {
const msg = 'Schema hasn\'t been registered for model "' + name + '".\n'
+ 'Use mongoose.model(name, schema)';
super(msg);
}
}
Object.defineProperty(MissingSchemaError.prototype, 'name', {
value: 'MissingSchemaError'
});
/*!
* exports
*/
module.exports = MissingSchemaError;

View File

@@ -0,0 +1,13 @@
'use strict';
/*!
* ignore
*/
class MongooseError extends Error { }
Object.defineProperty(MongooseError.prototype, 'name', {
value: 'MongooseError'
});
module.exports = MongooseError;

47
backend/node_modules/mongoose/lib/error/notFound.js generated vendored Normal file
View File

@@ -0,0 +1,47 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
const util = require('util');
/**
* OverwriteModel Error constructor.
* @api private
*/
class DocumentNotFoundError extends MongooseError {
constructor(filter, model, numAffected, result) {
let msg;
const messages = MongooseError.messages;
if (messages.DocumentNotFoundError != null) {
msg = typeof messages.DocumentNotFoundError === 'function' ?
messages.DocumentNotFoundError(filter, model) :
messages.DocumentNotFoundError;
} else {
msg = 'No document found for query "' + util.inspect(filter) +
'" on model "' + model + '"';
}
super(msg);
this.result = result;
this.numAffected = numAffected;
this.filter = filter;
// Backwards compat
this.query = filter;
}
}
Object.defineProperty(DocumentNotFoundError.prototype, 'name', {
value: 'DocumentNotFoundError'
});
/*!
* exports
*/
module.exports = DocumentNotFoundError;

View File

@@ -0,0 +1,31 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* Strict mode error constructor
*
* @param {string} type
* @param {string} value
* @api private
*/
class ObjectExpectedError extends MongooseError {
constructor(path, val) {
const typeDescription = Array.isArray(val) ? 'array' : 'primitive value';
super('Tried to set nested object field `' + path +
`\` to ${typeDescription} \`` + val + '`');
this.path = path;
}
}
Object.defineProperty(ObjectExpectedError.prototype, 'name', {
value: 'ObjectExpectedError'
});
module.exports = ObjectExpectedError;

View File

@@ -0,0 +1,31 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* Constructor for errors that happen when a parameter that's expected to be
* an object isn't an object
*
* @param {any} value
* @param {string} paramName
* @param {string} fnName
* @api private
*/
class ObjectParameterError extends MongooseError {
constructor(value, paramName, fnName) {
super('Parameter "' + paramName + '" to ' + fnName +
'() must be an object, got "' + (value == null ? value : value.toString()) + '" (type ' + typeof value + ')');
}
}
Object.defineProperty(ObjectParameterError.prototype, 'name', {
value: 'ObjectParameterError'
});
module.exports = ObjectParameterError;

View File

@@ -0,0 +1,31 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* OverwriteModel Error constructor.
* @param {string} name
* @api private
*/
class OverwriteModelError extends MongooseError {
constructor(name) {
super('Cannot overwrite `' + name + '` model once compiled.');
}
}
Object.defineProperty(OverwriteModelError.prototype, 'name', {
value: 'OverwriteModelError'
});
/*!
* exports
*/
module.exports = OverwriteModelError;

View File

@@ -0,0 +1,33 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
/**
* ParallelSave Error constructor.
*
* @param {Document} doc
* @api private
*/
class ParallelSaveError extends MongooseError {
constructor(doc) {
const msg = 'Can\'t save() the same doc multiple times in parallel. Document: ';
super(msg + doc._doc._id);
}
}
Object.defineProperty(ParallelSaveError.prototype, 'name', {
value: 'ParallelSaveError'
});
/*!
* exports
*/
module.exports = ParallelSaveError;

View File

@@ -0,0 +1,33 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
/**
* ParallelValidate Error constructor.
*
* @param {Document} doc
* @api private
*/
class ParallelValidateError extends MongooseError {
constructor(doc) {
const msg = 'Can\'t validate() the same doc multiple times in parallel. Document: ';
super(msg + doc._doc._id);
}
}
Object.defineProperty(ParallelValidateError.prototype, 'name', {
value: 'ParallelValidateError'
});
/*!
* exports
*/
module.exports = ParallelValidateError;

View File

@@ -0,0 +1,62 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
const allServersUnknown = require('../helpers/topology/allServersUnknown');
const isAtlas = require('../helpers/topology/isAtlas');
const isSSLError = require('../helpers/topology/isSSLError');
/*!
* ignore
*/
const atlasMessage = 'Could not connect to any servers in your MongoDB Atlas cluster. ' +
'One common reason is that you\'re trying to access the database from ' +
'an IP that isn\'t whitelisted. Make sure your current IP address is on your Atlas ' +
'cluster\'s IP whitelist: https://www.mongodb.com/docs/atlas/security-whitelist/';
const sslMessage = 'Mongoose is connecting with SSL enabled, but the server is ' +
'not accepting SSL connections. Please ensure that the MongoDB server you are ' +
'connecting to is configured to accept SSL connections. Learn more: ' +
'https://mongoosejs.com/docs/tutorials/ssl.html';
class MongooseServerSelectionError extends MongooseError {
/**
* MongooseServerSelectionError constructor
*
* @api private
*/
assimilateError(err) {
const reason = err.reason;
// Special message for a case that is likely due to IP whitelisting issues.
const isAtlasWhitelistError = isAtlas(reason) &&
allServersUnknown(reason) &&
err.message.indexOf('bad auth') === -1 &&
err.message.indexOf('Authentication failed') === -1;
if (isAtlasWhitelistError) {
this.message = atlasMessage;
} else if (isSSLError(reason)) {
this.message = sslMessage;
} else {
this.message = err.message;
}
for (const key in err) {
if (key !== 'name') {
this[key] = err[key];
}
}
this.cause = reason;
return this;
}
}
Object.defineProperty(MongooseServerSelectionError.prototype, 'name', {
value: 'MongooseServerSelectionError'
});
module.exports = MongooseServerSelectionError;

View File

@@ -0,0 +1,103 @@
/*!
* Module requirements
*/
'use strict';
const MongooseError = require('./mongooseError');
const util = require('util');
const combinePathErrors = require('../helpers/error/combinePathErrors');
/**
* Mongoose.set Error
*
* @api private
* @inherits MongooseError
*/
class SetOptionError extends MongooseError {
constructor() {
super('');
this.errors = {};
}
/**
* Console.log helper
*/
toString() {
return combinePathErrors(this);
}
/**
* inspect helper
* @api private
*/
inspect() {
return Object.assign(new Error(this.message), this);
}
/**
* add message
* @param {string} key
* @param {string|Error} error
* @api private
*/
addError(key, error) {
if (error instanceof SetOptionError) {
const { errors } = error;
for (const optionKey of Object.keys(errors)) {
this.addError(optionKey, errors[optionKey]);
}
return;
}
this.errors[key] = error;
this.message = combinePathErrors(this);
}
}
if (util.inspect.custom) {
// Avoid Node deprecation warning DEP0079
SetOptionError.prototype[util.inspect.custom] = SetOptionError.prototype.inspect;
}
/**
* Helper for JSON.stringify
* Ensure `name` and `message` show up in toJSON output re: gh-9847
* @api private
*/
Object.defineProperty(SetOptionError.prototype, 'toJSON', {
enumerable: false,
writable: false,
configurable: true,
value: function() {
return Object.assign({}, this, { name: this.name, message: this.message });
}
});
Object.defineProperty(SetOptionError.prototype, 'name', {
value: 'SetOptionError'
});
class SetOptionInnerError extends MongooseError {
/**
* Error for the "errors" array in "SetOptionError" with consistent message
* @param {string} key
*/
constructor(key) {
super(`"${key}" is not a valid option to set`);
}
}
SetOptionError.SetOptionInnerError = SetOptionInnerError;
/*!
* Module exports
*/
module.exports = SetOptionError;

35
backend/node_modules/mongoose/lib/error/strict.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* Strict mode error constructor
*
* @param {string} path
* @param {string} [msg]
* @param {boolean} [immutable]
* @inherits MongooseError
* @api private
*/
class StrictModeError extends MongooseError {
constructor(path, msg, immutable) {
msg = msg || 'Field `' + path + '` is not in schema and strict ' +
'mode is set to throw.';
super(msg);
this.isImmutableError = !!immutable;
this.path = path;
}
}
Object.defineProperty(StrictModeError.prototype, 'name', {
value: 'StrictModeError'
});
module.exports = StrictModeError;

View File

@@ -0,0 +1,31 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* Strict mode error constructor
*
* @param {string} path
* @param {string} [msg]
* @inherits MongooseError
* @api private
*/
class StrictPopulateError extends MongooseError {
constructor(path, msg) {
msg = msg || 'Cannot populate path `' + path + '` because it is not in your schema. ' + 'Set the `strictPopulate` option to false to override.';
super(msg);
this.path = path;
}
}
Object.defineProperty(StrictPopulateError.prototype, 'name', {
value: 'StrictPopulateError'
});
module.exports = StrictPopulateError;

30
backend/node_modules/mongoose/lib/error/syncIndexes.js generated vendored Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
/**
* SyncIndexes Error constructor.
*
* @param {string} message
* @param {string} errorsMap
* @inherits MongooseError
* @api private
*/
class SyncIndexesError extends MongooseError {
constructor(message, errorsMap) {
super(message);
this.errors = errorsMap;
}
}
Object.defineProperty(SyncIndexesError.prototype, 'name', {
value: 'SyncIndexesError'
});
module.exports = SyncIndexesError;

97
backend/node_modules/mongoose/lib/error/validation.js generated vendored Normal file
View File

@@ -0,0 +1,97 @@
/*!
* Module requirements
*/
'use strict';
const MongooseError = require('./mongooseError');
const getConstructorName = require('../helpers/getConstructorName');
const util = require('util');
const combinePathErrors = require('../helpers/error/combinePathErrors');
/**
* Document Validation Error
*
* @api private
* @param {Document} [instance]
* @inherits MongooseError
*/
class ValidationError extends MongooseError {
constructor(instance) {
let _message;
if (getConstructorName(instance) === 'model') {
_message = instance.constructor.modelName + ' validation failed';
} else {
_message = 'Validation failed';
}
super(_message);
this.errors = {};
this._message = _message;
if (instance) {
instance.$errors = this.errors;
}
}
/**
* Console.log helper
*/
toString() {
return this.name + ': ' + combinePathErrors(this);
}
/**
* add message
* @param {string} path
* @param {string|Error} error
* @api private
*/
addError(path, error) {
if (error instanceof ValidationError) {
const { errors } = error;
for (const errorPath of Object.keys(errors)) {
this.addError(`${path}.${errorPath}`, errors[errorPath]);
}
return;
}
this.errors[path] = error;
this.message = this._message + ': ' + combinePathErrors(this);
}
}
if (util.inspect.custom) {
// Avoid Node deprecation warning DEP0079
ValidationError.prototype[util.inspect.custom] = ValidationError.prototype.inspect;
}
/**
* Helper for JSON.stringify
* Ensure `name` and `message` show up in toJSON output re: gh-9847
* @api private
*/
Object.defineProperty(ValidationError.prototype, 'toJSON', {
enumerable: false,
writable: false,
configurable: true,
value: function() {
return Object.assign({}, this, { name: this.name, message: this.message });
}
});
Object.defineProperty(ValidationError.prototype, 'name', {
value: 'ValidationError'
});
/*!
* Module exports
*/
module.exports = ValidationError;

100
backend/node_modules/mongoose/lib/error/validator.js generated vendored Normal file
View File

@@ -0,0 +1,100 @@
/*!
* Module dependencies.
*/
'use strict';
const MongooseError = require('./mongooseError');
/**
* Schema validator error
*
* @param {object} properties
* @param {Document} doc
* @api private
*/
class ValidatorError extends MongooseError {
constructor(properties, doc) {
let msg = properties.message;
if (!msg) {
msg = MongooseError.messages.general.default;
}
const message = formatMessage(msg, properties, doc);
super(message);
properties = Object.assign({}, properties, { message: message });
this.properties = properties;
this.kind = properties.type;
this.path = properties.path;
this.value = properties.value;
this.reason = properties.reason;
}
/**
* toString helper
* TODO remove? This defaults to `${this.name}: ${this.message}`
* @api private
*/
toString() {
return this.message;
}
/**
* Ensure `name` and `message` show up in toJSON output re: gh-9296
* @api private
*/
toJSON() {
return Object.assign({ name: this.name, message: this.message }, this);
}
}
Object.defineProperty(ValidatorError.prototype, 'name', {
value: 'ValidatorError'
});
/**
* The object used to define this validator. Not enumerable to hide
* it from `require('util').inspect()` output re: gh-3925
* @api private
*/
Object.defineProperty(ValidatorError.prototype, 'properties', {
enumerable: false,
writable: true,
value: null
});
// Exposed for testing
ValidatorError.prototype.formatMessage = formatMessage;
/**
* Formats error messages
* @api private
*/
function formatMessage(msg, properties, doc) {
if (typeof msg === 'function') {
return msg(properties, doc);
}
const propertyNames = Object.keys(properties);
for (const propertyName of propertyNames) {
if (propertyName === 'message') {
continue;
}
msg = msg.replace('{' + propertyName.toUpperCase() + '}', properties[propertyName]);
}
return msg;
}
/*!
* exports
*/
module.exports = ValidatorError;

38
backend/node_modules/mongoose/lib/error/version.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
/*!
* Module dependencies.
*/
const MongooseError = require('./mongooseError');
/**
* Version Error constructor.
*
* @param {Document} doc
* @param {number} currentVersion
* @param {string[]} modifiedPaths
* @api private
*/
class VersionError extends MongooseError {
constructor(doc, currentVersion, modifiedPaths) {
const modifiedPathsStr = modifiedPaths.join(', ');
super('No matching document found for id "' + doc._doc._id +
'" version ' + currentVersion + ' modifiedPaths "' + modifiedPathsStr + '"');
this.version = currentVersion;
this.modifiedPaths = modifiedPaths;
}
}
Object.defineProperty(VersionError.prototype, 'name', {
value: 'VersionError'
});
/*!
* exports
*/
module.exports = VersionError;

View File

@@ -0,0 +1,39 @@
'use strict';
module.exports = function prepareDiscriminatorPipeline(pipeline, schema, prefix) {
const discriminatorMapping = schema?.discriminatorMapping;
prefix = prefix || '';
if (discriminatorMapping && !discriminatorMapping.isRoot) {
const originalPipeline = pipeline;
const filterKey = (prefix.length > 0 ? prefix + '.' : prefix) + discriminatorMapping.key;
const discriminatorValue = discriminatorMapping.value;
// If the first pipeline stage is a match and it doesn't specify a `__t`
// key, add the discriminator key to it. This allows for potential
// aggregation query optimizations not to be disturbed by this feature.
if (originalPipeline[0] != null &&
originalPipeline[0].$match &&
(originalPipeline[0].$match[filterKey] === undefined || originalPipeline[0].$match[filterKey] === discriminatorValue)) {
originalPipeline[0].$match[filterKey] = discriminatorValue;
// `originalPipeline` is a ref, so there's no need for
// aggregate._pipeline = originalPipeline
} else if (originalPipeline[0]?.$geoNear) {
originalPipeline[0].$geoNear.query =
originalPipeline[0].$geoNear.query || {};
originalPipeline[0].$geoNear.query[filterKey] = discriminatorValue;
} else if (originalPipeline[0]?.$search) {
if (originalPipeline[1]?.$match != null) {
originalPipeline[1].$match[filterKey] = originalPipeline[1].$match[filterKey] || discriminatorValue;
} else {
const match = {};
match[filterKey] = discriminatorValue;
originalPipeline.splice(1, 0, { $match: match });
}
} else {
const match = {};
match[filterKey] = discriminatorValue;
originalPipeline.unshift({ $match: match });
}
}
};

View File

@@ -0,0 +1,50 @@
'use strict';
module.exports = function stringifyFunctionOperators(pipeline) {
if (!Array.isArray(pipeline)) {
return;
}
for (const stage of pipeline) {
if (stage == null) {
continue;
}
const canHaveAccumulator = stage.$group || stage.$bucket || stage.$bucketAuto;
if (canHaveAccumulator != null) {
for (const key of Object.keys(canHaveAccumulator)) {
handleAccumulator(canHaveAccumulator[key]);
}
}
const stageType = Object.keys(stage)[0];
if (stageType && typeof stage[stageType] === 'object') {
const stageOptions = stage[stageType];
for (const key of Object.keys(stageOptions)) {
if (stageOptions[key] != null &&
stageOptions[key].$function != null &&
typeof stageOptions[key].$function.body === 'function') {
stageOptions[key].$function.body = stageOptions[key].$function.body.toString();
}
}
}
if (stage.$facet != null) {
for (const key of Object.keys(stage.$facet)) {
stringifyFunctionOperators(stage.$facet[key]);
}
}
}
};
function handleAccumulator(operator) {
if (operator == null || operator.$accumulator == null) {
return;
}
for (const key of ['init', 'accumulate', 'merge', 'finalize']) {
if (typeof operator.$accumulator[key] === 'function') {
operator.$accumulator[key] = String(operator.$accumulator[key]);
}
}
}

View File

@@ -0,0 +1,33 @@
'use strict';
module.exports = arrayDepth;
function arrayDepth(arr) {
if (!Array.isArray(arr)) {
return { min: 0, max: 0, containsNonArrayItem: true };
}
if (arr.length === 0) {
return { min: 1, max: 1, containsNonArrayItem: false };
}
if (arr.length === 1 && !Array.isArray(arr[0])) {
return { min: 1, max: 1, containsNonArrayItem: false };
}
const res = arrayDepth(arr[0]);
for (let i = 1; i < arr.length; ++i) {
const _res = arrayDepth(arr[i]);
if (_res.min < res.min) {
res.min = _res.min;
}
if (_res.max > res.max) {
res.max = _res.max;
}
res.containsNonArrayItem = res.containsNonArrayItem || _res.containsNonArrayItem;
}
res.min = res.min + 1;
res.max = res.max + 1;
return res;
}

View File

@@ -0,0 +1,24 @@
'use strict';
const symbols = require('../schema/symbols');
/**
* Filter predicate that returns true for built-in middleware (marked with `builtInMiddleware` symbol).
*/
const isBuiltInMiddleware = hook => hook.fn[symbols.builtInMiddleware];
/**
* Builds a filter for kareem's execPre/execPost based on middleware options.
*
* @param {object} options - Options object that may contain `middleware` setting
* @param {string} phase - Either 'pre' or 'post'
* @returns {Function|null} - null runs all middleware, isBuiltInMiddleware skips user middleware
*/
function buildMiddlewareFilter(options, phase) {
const shouldRun = options?.middleware?.[phase] ?? options?.middleware ?? true;
return shouldRun ? null : isBuiltInMiddleware;
}
module.exports = {
buildMiddlewareFilter
};

236
backend/node_modules/mongoose/lib/helpers/clone.js generated vendored Normal file
View File

@@ -0,0 +1,236 @@
'use strict';
const Decimal = require('../types/decimal128');
const ObjectId = require('../types/objectid');
const specialProperties = require('./specialProperties');
const isMongooseObject = require('./isMongooseObject');
const getFunctionName = require('./getFunctionName');
const isBsonType = require('./isBsonType');
const isMongooseArray = require('../types/array/isMongooseArray').isMongooseArray;
const isObject = require('./isObject');
const isPOJO = require('./isPOJO');
const symbols = require('./symbols');
const trustedSymbol = require('./query/trusted').trustedSymbol;
const BSON = require('mongodb/lib/bson');
const UUID = BSON.UUID;
const Binary = BSON.Binary;
/**
* Object clone with Mongoose natives support.
*
* If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
*
* Functions and primitives are never cloned.
*
* @param {object} obj the object to clone
* @param {object} options
* @param {boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize.
* @return {object} the cloned object
* @api private
*/
function clone(obj, options, isArrayChild) {
if (obj == null) {
return obj;
}
if (isBsonType(obj, 'Double')) {
return new BSON.Double(obj.value);
}
if (typeof obj === 'number' || typeof obj === 'string' || typeof obj === 'boolean' || typeof obj === 'bigint') {
return obj;
}
if (Array.isArray(obj)) {
return cloneArray(obj, options);
}
if (isMongooseObject(obj)) {
if (options) {
if (options.flattenUUIDs) {
if (obj instanceof Binary && obj._subtype === Binary.SUBTYPE_UUID) {
return obj.toString();
}
if (obj?.isMongooseBuffer && obj._subtype === Binary.SUBTYPE_UUID) {
return obj.toUUID().toString();
}
}
if (options.retainDocuments && obj.$__ != null) {
const clonedDoc = obj.$clone();
if (obj.__index != null) {
clonedDoc.__index = obj.__index;
}
if (obj.__parentArray != null) {
clonedDoc.__parentArray = options.parentArray ?? obj.__parentArray;
}
clonedDoc.$__setParent(options.parentDoc ?? obj.$__parent);
return clonedDoc;
}
if (options.retainDocuments && obj.$isMongooseMap) {
const clonedParent = options.parentDoc ?? obj.$__parent;
const MongooseMap = obj.constructor;
const ret = new MongooseMap({}, obj.$__path, clonedParent, obj.$__schemaType);
for (const [key, value] of obj) {
ret.$__set(key, clone(value, { ...options, parentDoc: clonedParent }));
}
return ret;
}
}
if (isPOJO(obj) && obj.$__ != null && obj._doc != null) {
return obj._doc;
}
let ret;
if (options?.json && typeof obj.toJSON === 'function') {
ret = obj.toJSON(options);
} else {
ret = obj.toObject(options);
}
return ret;
}
const objConstructor = obj.constructor;
if (objConstructor) {
switch (getFunctionName(objConstructor)) {
case 'Object':
return cloneObject(obj, options, isArrayChild);
case 'Date':
return new objConstructor(+obj);
case 'RegExp':
return cloneRegExp(obj);
default:
// ignore
break;
}
}
if (isBsonType(obj, 'ObjectId')) {
if (options?.flattenObjectIds) {
return obj.toJSON();
}
return new ObjectId(obj.id);
}
if (isBsonType(obj, 'Decimal128')) {
if (options?.flattenDecimals) {
return obj.toJSON();
}
return Decimal.fromString(obj.toString());
}
if (obj instanceof UUID) {
if (options?.flattenUUIDs) {
return obj.toJSON();
}
return new UUID(obj.buffer);
}
// object created with Object.create(null)
if (!objConstructor && isObject(obj)) {
return cloneObject(obj, options, isArrayChild);
}
if (typeof obj === 'object' && obj[symbols.schemaTypeSymbol]) {
return obj.clone();
}
// If we're cloning this object to go into a MongoDB command,
// and there's a `toBSON()` function, assume this object will be
// stored as a primitive in MongoDB and doesn't need to be cloned.
if (options?.bson && typeof obj.toBSON === 'function') {
return obj;
}
if (typeof obj.valueOf === 'function') {
return obj.valueOf();
}
return cloneObject(obj, options, isArrayChild);
}
module.exports = clone;
/*!
* ignore
*/
function cloneObject(obj, options, isArrayChild) {
const minimize = options?.minimize;
const omitUndefined = options?.omitUndefined;
const seen = options?._seen;
const ret = {};
let hasKeys;
if (seen && seen.has(obj)) {
return seen.get(obj);
} else if (seen) {
seen.set(obj, ret);
}
if (trustedSymbol in obj && options?.copyTrustedSymbol !== false) {
ret[trustedSymbol] = obj[trustedSymbol];
}
const keys = Object.keys(obj);
const len = keys.length;
for (let i = 0; i < len; ++i) {
const key = keys[i];
if (specialProperties.has(key)) {
continue;
}
// Don't pass `isArrayChild` down
const val = clone(obj[key], options, false);
if ((minimize === false || omitUndefined) && typeof val === 'undefined') {
delete ret[key];
} else if (minimize !== true || (typeof val !== 'undefined')) {
hasKeys || (hasKeys = true);
ret[key] = val;
}
}
return minimize && !isArrayChild ? hasKeys && ret : ret;
}
function cloneArray(arr, options) {
let i = 0;
const len = arr.length;
let ret = null;
if (options?.retainDocuments) {
if (arr.isMongooseDocumentArray) {
ret = new (arr.$schemaType().schema.base.Types.DocumentArray)([], arr.$path(), arr.$parent(), arr.$schemaType());
} else if (arr.isMongooseArray) {
ret = new (arr.$parent().schema.base.Types.Array)([], arr.$path(), arr.$parent(), arr.$schemaType());
} else {
ret = new Array(len);
}
} else {
ret = new Array(len);
}
arr = isMongooseArray(arr) ? arr.__array : arr;
if (ret.isMongooseDocumentArray) {
// Create new options object to avoid mutating the shared options.
// Subdocs need parentArray to point to their own cloned array.
options = { ...options, parentArray: ret };
}
for (i = 0; i < len; ++i) {
ret[i] = clone(arr[i], options, true);
}
return ret;
}
function cloneRegExp(regexp) {
const ret = new RegExp(regexp.source, regexp.flags);
if (ret.lastIndex !== regexp.lastIndex) {
ret.lastIndex = regexp.lastIndex;
}
return ret;
}

127
backend/node_modules/mongoose/lib/helpers/common.js generated vendored Normal file
View File

@@ -0,0 +1,127 @@
'use strict';
/*!
* Module dependencies.
*/
const Binary = require('mongodb/lib/bson').Binary;
const isBsonType = require('./isBsonType');
const isMongooseObject = require('./isMongooseObject');
const MongooseError = require('../error');
const util = require('util');
exports.flatten = flatten;
exports.modifiedPaths = modifiedPaths;
/*!
* ignore
*/
function flatten(update, path, options, schema) {
let keys;
if (update && isMongooseObject(update) && !Buffer.isBuffer(update)) {
keys = Object.keys(update.toObject({ transform: false, virtuals: false }) || {});
} else {
keys = Object.keys(update || {});
}
const numKeys = keys.length;
const result = {};
path = path ? path + '.' : '';
for (let i = 0; i < numKeys; ++i) {
const key = keys[i];
const val = update[key];
result[path + key] = val;
// Avoid going into mixed paths if schema is specified
const keySchema = schema?.path?.(path + key);
const isNested = schema?.nested?.[path + key];
if (keySchema?.instance === 'Mixed') continue;
if (shouldFlatten(val)) {
if (options?.skipArrays && Array.isArray(val)) {
continue;
}
const flat = flatten(val, path + key, options, schema);
for (const k in flat) {
result[k] = flat[k];
}
if (Array.isArray(val)) {
result[path + key] = val;
}
}
if (isNested) {
const paths = Object.keys(schema.paths);
for (const p of paths) {
if (p.startsWith(path + key + '.') && !Object.hasOwn(result, p)) {
result[p] = void 0;
}
}
}
}
return result;
}
/*!
* ignore
*/
function modifiedPaths(update, path, result, recursion = null) {
if (update == null || typeof update !== 'object') {
return;
}
if (recursion == null) {
recursion = {
raw: { update, path },
trace: new WeakSet()
};
}
if (recursion.trace.has(update)) {
throw new MongooseError(`a circular reference in the update value, updateValue:
${util.inspect(recursion.raw.update, { showHidden: false, depth: 1 })}
updatePath: '${recursion.raw.path}'`);
}
recursion.trace.add(update);
const keys = Object.keys(update || {});
const numKeys = keys.length;
result = result || {};
path = path ? path + '.' : '';
for (let i = 0; i < numKeys; ++i) {
const key = keys[i];
let val = update[key];
const _path = path + key;
result[_path] = true;
if (!Buffer.isBuffer(val) && isMongooseObject(val)) {
val = val.toObject({ transform: false, virtuals: false });
}
if (shouldFlatten(val)) {
modifiedPaths(val, path + key, result, recursion);
}
}
recursion.trace.delete(update);
return result;
}
/*!
* ignore
*/
function shouldFlatten(val) {
return val &&
typeof val === 'object' &&
!(val instanceof Date) &&
!isBsonType(val, 'ObjectId') &&
(!Array.isArray(val) || val.length !== 0) &&
!(val instanceof Buffer) &&
!isBsonType(val, 'Decimal128') &&
!(val instanceof Binary);
}

View File

@@ -0,0 +1,24 @@
'use strict';
/**
* Handles creating `{ type: 'object' }` vs `{ bsonType: 'object' }` vs `{ bsonType: ['object', 'null'] }`
*
* @param {string} type
* @param {string} bsonType
* @param {boolean} useBsonType
* @param {boolean} isRequired
*/
module.exports = function createJSONSchemaTypeArray(type, bsonType, useBsonType, isRequired) {
if (useBsonType) {
if (isRequired) {
return { bsonType };
}
return { bsonType: [bsonType, 'null'] };
} else {
if (isRequired) {
return { type };
}
return { type: [type, 'null'] };
}
};

View File

@@ -0,0 +1,225 @@
'use strict';
/*!
* Module dependencies.
*/
const EachAsyncMultiError = require('../../error/eachAsyncMultiError');
const immediate = require('../immediate');
/**
* Execute `fn` for every document in the cursor. If `fn` returns a promise,
* will wait for the promise to resolve before iterating on to the next one.
* Returns a promise that resolves when done.
*
* @param {Function} next the thunk to call to get the next document
* @param {Function} fn
* @param {object} options
* @param {number} [options.batchSize=null] if set, Mongoose will call `fn` with an array of at most `batchSize` documents, instead of a single document
* @param {number} [options.parallel=1] maximum number of `fn` calls that Mongoose will run in parallel
* @param {AbortSignal} [options.signal] allow cancelling this eachAsync(). Once the abort signal is fired, `eachAsync()` will immediately fulfill the returned promise (or call the callback) and not fetch any more documents.
* @return {Promise}
* @api public
* @method eachAsync
*/
module.exports = async function eachAsync(next, fn, options) {
const parallel = options.parallel || 1;
const batchSize = options.batchSize;
const signal = options.signal;
const continueOnError = options.continueOnError;
const aggregatedErrors = [];
const enqueue = asyncQueue();
let aborted = false;
return new Promise((resolve, reject) => {
if (signal != null) {
if (signal.aborted) {
return resolve(null);
}
signal.addEventListener('abort', () => {
aborted = true;
return resolve(null);
}, { once: true });
}
if (batchSize != null) {
if (typeof batchSize !== 'number') {
throw new TypeError('batchSize must be a number');
} else if (!Number.isInteger(batchSize)) {
throw new TypeError('batchSize must be an integer');
} else if (batchSize < 1) {
throw new TypeError('batchSize must be at least 1');
}
}
iterate((err, res) => {
if (err != null) {
return reject(err);
}
resolve(res);
});
});
function iterate(finalCallback) {
let handleResultsInProgress = 0;
let currentDocumentIndex = 0;
let error = null;
for (let i = 0; i < parallel; ++i) {
enqueue(createFetch());
}
function createFetch() {
let documentsBatch = [];
let drained = false;
return fetch;
function fetch(done) {
if (drained || aborted) {
return done();
} else if (error) {
return done();
}
next(function(err, doc) {
if (error != null) {
return done();
}
if (err != null) {
if (err.name === 'MongoCursorExhaustedError') {
// We may end up calling `next()` multiple times on an exhausted
// cursor, which leads to an error. In case cursor is exhausted,
// just treat it as if the cursor returned no document, which is
// how a cursor indicates it is exhausted.
doc = null;
} else if (continueOnError) {
aggregatedErrors.push(err);
} else {
error = err;
finalCallback(err);
return done();
}
}
if (doc == null) {
drained = true;
if (handleResultsInProgress <= 0) {
const finalErr = continueOnError ?
createEachAsyncMultiError(aggregatedErrors) :
error;
finalCallback(finalErr);
} else if (batchSize && documentsBatch.length) {
handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
}
return done();
}
++handleResultsInProgress;
// Kick off the subsequent `next()` before handling the result, but
// make sure we know that we still have a result to handle re: #8422
immediate(() => done());
if (batchSize) {
documentsBatch.push(doc);
}
// If the current documents size is less than the provided batch size don't process the documents yet
if (batchSize && documentsBatch.length !== batchSize) {
immediate(() => enqueue(fetch));
return;
}
const docsToProcess = batchSize ? documentsBatch : doc;
function handleNextResultCallBack(err) {
if (batchSize) {
handleResultsInProgress -= documentsBatch.length;
documentsBatch = [];
} else {
--handleResultsInProgress;
}
if (err != null) {
if (continueOnError) {
aggregatedErrors.push(err);
} else {
error = err;
return finalCallback(err);
}
}
if ((drained || aborted) && handleResultsInProgress <= 0) {
const finalErr = continueOnError ?
createEachAsyncMultiError(aggregatedErrors) :
error;
return finalCallback(finalErr);
}
immediate(() => enqueue(fetch));
}
handleNextResult(docsToProcess, currentDocumentIndex++, handleNextResultCallBack);
});
}
}
}
function handleNextResult(doc, i, callback) {
let maybePromise;
try {
maybePromise = fn(doc, i);
} catch (err) {
return callback(err);
}
if (typeof maybePromise?.then === 'function') {
maybePromise.then(
function() { callback(null); },
function(error) {
callback(error || new Error('`eachAsync()` promise rejected without error'));
});
} else {
callback(null);
}
}
};
// `next()` can only execute one at a time, so make sure we always execute
// `next()` in series, while still allowing multiple `fn()` instances to run
// in parallel.
function asyncQueue() {
const _queue = [];
let inProgress = null;
let id = 0;
return function enqueue(fn) {
if (
inProgress === null &&
_queue.length === 0
) {
inProgress = id++;
return fn(_step);
}
_queue.push(fn);
};
function _step() {
if (_queue.length !== 0) {
inProgress = id++;
const fn = _queue.shift();
fn(_step);
} else {
inProgress = null;
}
}
}
function createEachAsyncMultiError(aggregatedErrors) {
if (aggregatedErrors.length === 0) {
return null;
}
return new EachAsyncMultiError(aggregatedErrors);
}

View File

@@ -0,0 +1,36 @@
'use strict';
module.exports = applyEmbeddedDiscriminators;
function applyEmbeddedDiscriminators(schema, seen = new WeakSet(), overwriteExisting = false) {
if (seen.has(schema)) {
return;
}
seen.add(schema);
for (const path of Object.keys(schema.paths)) {
const schemaType = schema.paths[path];
if (!schemaType.schema) {
continue;
}
applyEmbeddedDiscriminators(schemaType.schema, seen);
if (!schemaType.schema._applyDiscriminators) {
continue;
}
if (schemaType._appliedDiscriminators && !overwriteExisting) {
continue;
}
for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) {
const {
schema: discriminatorSchema,
options
} = schemaType.schema._applyDiscriminators.get(discriminatorKey);
applyEmbeddedDiscriminators(discriminatorSchema, seen);
schemaType.discriminator(
discriminatorKey,
discriminatorSchema,
overwriteExisting ? { ...options, overwriteExisting: true } : options
);
}
schemaType._appliedDiscriminators = true;
}
}

View File

@@ -0,0 +1,16 @@
'use strict';
const isBsonType = require('../isBsonType');
module.exports = function areDiscriminatorValuesEqual(a, b) {
if (typeof a === 'string' && typeof b === 'string') {
return a === b;
}
if (typeof a === 'number' && typeof b === 'number') {
return a === b;
}
if (isBsonType(a, 'ObjectId') && isBsonType(b, 'ObjectId')) {
return a.toString() === b.toString();
}
return false;
};

View File

@@ -0,0 +1,12 @@
'use strict';
module.exports = function checkEmbeddedDiscriminatorKeyProjection(userProjection, path, schema, selected, addedPaths) {
const userProjectedInPath = Object.keys(userProjection).
reduce((cur, key) => cur || key.startsWith(path + '.'), false);
const _discriminatorKey = path + '.' + schema.options.discriminatorKey;
if (!userProjectedInPath &&
addedPaths.length === 1 &&
addedPaths[0] === _discriminatorKey) {
selected.splice(selected.indexOf(_discriminatorKey), 1);
}
};

View File

@@ -0,0 +1,29 @@
'use strict';
const getDiscriminatorByValue = require('./getDiscriminatorByValue');
/**
* Find the correct constructor, taking into account discriminators
* @api private
*/
module.exports = function getConstructor(Constructor, value, defaultDiscriminatorValue) {
const discriminatorKey = Constructor.schema.options.discriminatorKey;
let discriminatorValue = value?.[discriminatorKey];
if (discriminatorValue == null) {
discriminatorValue = defaultDiscriminatorValue;
}
if (Constructor.discriminators &&
discriminatorValue != null) {
if (Constructor.discriminators[discriminatorValue]) {
Constructor = Constructor.discriminators[discriminatorValue];
} else {
const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, discriminatorValue);
if (constructorByValue) {
Constructor = constructorByValue;
}
}
}
return Constructor;
};

View File

@@ -0,0 +1,28 @@
'use strict';
const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual');
/**
* returns discriminator by discriminatorMapping.value
*
* @param {object} discriminators
* @param {string} value
* @api private
*/
module.exports = function getDiscriminatorByValue(discriminators, value) {
if (discriminators == null) {
return null;
}
for (const name of Object.keys(discriminators)) {
const it = discriminators[name];
if (
it.schema &&
it.schema.discriminatorMapping &&
areDiscriminatorValuesEqual(it.schema.discriminatorMapping.value, value)
) {
return it;
}
}
return null;
};

View File

@@ -0,0 +1,27 @@
'use strict';
const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual');
/**
* returns discriminator by discriminatorMapping.value
*
* @param {Schema} schema
* @param {string} value
* @api private
*/
module.exports = function getSchemaDiscriminatorByValue(schema, value) {
if (schema?.discriminators == null) {
return null;
}
for (const key of Object.keys(schema.discriminators)) {
const discriminatorSchema = schema.discriminators[key];
if (discriminatorSchema.discriminatorMapping == null) {
continue;
}
if (areDiscriminatorValuesEqual(discriminatorSchema.discriminatorMapping.value, value)) {
return discriminatorSchema;
}
}
return null;
};

View File

@@ -0,0 +1,96 @@
'use strict';
const schemaMerge = require('../schema/merge');
const specialProperties = require('../../helpers/specialProperties');
const isBsonType = require('../../helpers/isBsonType');
const ObjectId = require('../../types/objectid');
const SchemaType = require('../../schemaType');
const isObject = require('../../helpers/isObject');
/**
* Merges `from` into `to` without overwriting existing properties.
*
* @param {object} to
* @param {object} from
* @param {string} [path]
* @api private
*/
module.exports = function mergeDiscriminatorSchema(to, from, path, seen) {
const keys = Object.keys(from);
let i = 0;
const len = keys.length;
let key;
path = path || '';
seen = seen || new WeakSet();
if (seen.has(from)) {
return;
}
seen.add(from);
while (i < len) {
key = keys[i++];
if (!path) {
if (key === 'discriminators' ||
key === 'base' ||
key === '_applyDiscriminators' ||
key === '_userProvidedOptions' ||
key === 'options' ||
key === 'tree') {
continue;
}
}
if (path === 'tree' && from?.instanceOfSchema) {
continue;
}
if (specialProperties.has(key)) {
continue;
}
if (key === 'parentSchema') {
// Skip parentSchema to avoid merging parent schema multiple times
// through child paths' back-references (gh-15966)
continue;
}
if (to[key] == null) {
to[key] = from[key];
} else if (isObject(from[key])) {
if (!isObject(to[key])) {
to[key] = {};
}
if (from[key] != null) {
// Skip merging schemas if we're creating a discriminator schema and
// base schema has a given path as a single nested but discriminator schema
// has the path as a document array, or vice versa (gh-9534)
if ((from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
(from[key].$isMongooseDocumentArray && to[key].$isSingleNested) ||
(from[key].$isMongooseDocumentArrayElement && to[key].$isMongooseDocumentArrayElement)) {
continue;
} else if (from[key].instanceOfSchema) {
if (to[key].instanceOfSchema) {
schemaMerge(to[key], from[key].clone(), true);
} else {
to[key] = from[key].clone();
}
continue;
} else if (isBsonType(from[key], 'ObjectId')) {
to[key] = new ObjectId(from[key]);
continue;
} else if (from[key] instanceof SchemaType) {
if (to[key] == null) {
to[key] = from[key].clone();
}
// For container types with nested schemas, we need to continue to the
// recursive merge below to properly merge the nested schemas
if (!from[key].$isMongooseDocumentArray && !from[key].$isSingleNested) {
continue;
}
}
}
mergeDiscriminatorSchema(to[key], from[key], path ? path + '.' + key : key, seen);
}
}
if (from?.instanceOfSchema) {
to.tree = Object.assign({}, from.tree, to.tree);
}
};

View File

@@ -0,0 +1,132 @@
'use strict';
const isNestedProjection = require('../projection/isNestedProjection');
module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip, options) {
const paths = Object.keys(doc.$__schema.paths);
const plen = paths.length;
const skipParentChangeTracking = options?.skipParentChangeTracking;
for (let i = 0; i < plen; ++i) {
let def;
let curPath = '';
const p = paths[i];
if (p === '_id' && doc.$__.skipId) {
continue;
}
const type = doc.$__schema.paths[p];
const path = type.splitPath();
const len = path.length;
if (path[len - 1] === '$*') {
continue;
}
let included = false;
let doc_ = doc._doc;
for (let j = 0; j < len; ++j) {
if (doc_ == null) {
break;
}
const piece = path[j];
curPath += (!curPath.length ? '' : '.') + piece;
if (exclude === true) {
if (curPath in fields) {
break;
}
} else if (exclude === false && fields && !included) {
const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray;
if ((curPath in fields && !isNestedProjection(fields[curPath])) || (j === len - 1 && hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) {
included = true;
} else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) {
break;
}
}
if (j === len - 1) {
if (doc_[piece] !== void 0) {
break;
}
if (isBeforeSetters != null) {
if (typeof type.defaultValue === 'function') {
if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
break;
}
if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
break;
}
} else if (!isBeforeSetters) {
// Non-function defaults should always run **before** setters
continue;
}
}
if (pathsToSkip && pathsToSkip[curPath]) {
break;
}
if (fields && exclude !== null) {
if (exclude === true) {
// apply defaults to all non-excluded fields
if (p in fields) {
continue;
}
try {
def = type.getDefault(doc, false);
} catch (err) {
doc.invalidate(p, err);
break;
}
if (typeof def !== 'undefined') {
doc_[piece] = def;
applyChangeTracking(doc, p, skipParentChangeTracking);
}
} else if (included) {
// selected field
try {
def = type.getDefault(doc, false);
} catch (err) {
doc.invalidate(p, err);
break;
}
if (typeof def !== 'undefined') {
doc_[piece] = def;
applyChangeTracking(doc, p, skipParentChangeTracking);
}
}
} else {
try {
def = type.getDefault(doc, false);
} catch (err) {
doc.invalidate(p, err);
break;
}
if (typeof def !== 'undefined') {
doc_[piece] = def;
applyChangeTracking(doc, p, skipParentChangeTracking);
}
}
} else {
doc_ = doc_[piece];
}
}
}
};
/*!
* ignore
*/
function applyChangeTracking(doc, fullPath, skipParentChangeTracking) {
doc.$__.activePaths.default(fullPath);
if (!skipParentChangeTracking && doc.$isSubdocument && doc.$isSingleNested && doc.$parent() != null) {
doc.$parent().$__.activePaths.default(doc.$__pathRelativeToParent(fullPath));
}
}

View File

@@ -0,0 +1,106 @@
'use strict';
const handleTimestampOption = require('../schema/handleTimestampOption');
const mpath = require('mpath');
const utils = require('../../utils');
module.exports = applyTimestamps;
/**
* Apply a given schema's timestamps to the given POJO
*
* @param {Schema} schema
* @param {object} obj
* @param {object} [options]
* @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt
* @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time.
*/
function applyTimestamps(schema, obj, options) {
if (obj == null) {
return obj;
}
applyTimestampsToChildren(schema, obj, options);
return applyTimestampsToDoc(schema, obj, options);
}
/**
* Apply timestamps to any subdocuments
*
* @param {Schema} schema subdocument schema
* @param {object} res subdocument
* @param {object} [options]
* @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt
* @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time.
*/
function applyTimestampsToChildren(schema, res, options) {
for (const childSchema of schema.childSchemas) {
const _path = childSchema.model.path;
const _schema = childSchema.schema;
if (!_path) {
continue;
}
const _obj = mpath.get(_path, res);
if (_obj == null || (Array.isArray(_obj) && _obj.flat(Infinity).length === 0)) {
continue;
}
applyTimestamps(_schema, _obj, options);
}
}
/**
* Apply timestamps to a given document. Does not apply timestamps to subdocuments: use `applyTimestampsToChildren` instead
*
* @param {Schema} schema
* @param {object} obj
* @param {object} [options]
* @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt
* @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time.
*/
function applyTimestampsToDoc(schema, obj, options) {
if (obj == null || typeof obj !== 'object') {
return;
}
if (Array.isArray(obj)) {
for (const el of obj) {
applyTimestampsToDoc(schema, el, options);
}
return;
}
if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) {
for (const discriminatorKey of Object.keys(schema.discriminators)) {
const discriminator = schema.discriminators[discriminatorKey];
const key = discriminator.discriminatorMapping.key;
const value = discriminator.discriminatorMapping.value;
if (obj[key] == value) {
schema = discriminator;
break;
}
}
}
const createdAt = handleTimestampOption(schema.options.timestamps, 'createdAt');
const updatedAt = handleTimestampOption(schema.options.timestamps, 'updatedAt');
const currentTime = options?.currentTime;
let ts = null;
if (currentTime != null) {
ts = currentTime();
} else if (schema.base?.now) {
ts = schema.base.now();
} else {
ts = new Date();
}
if (createdAt && obj[createdAt] == null && !options?.isUpdate) {
obj[createdAt] = ts;
}
if (updatedAt) {
obj[updatedAt] = ts;
}
}

Some files were not shown because too many files have changed in this diff Show More