📦 chore: npm audit fixes and Mongoose 8.23 TypeScript follow-ups (#12996)

* chore: Update axios dependency to version 1.16.0 across multiple package files

* chore: Update express-rate-limit and ip-address dependencies to versions 8.5.1 and 10.2.0 in package-lock.json and package.json

* chore: Update mongoose and hono dependencies to versions 8.23.1 and 4.12.18 across multiple package files

* fix: Add type parameters to mongoose lean queries in accessRole and aclEntry methods

* fix: Add type parameters to mongoose lean queries in action, agent, and agentCategory methods

* chore: Update moduleResolution to 'bundler' in tsconfig.json for api and data-schemas packages

* fix: Update mongoose lean queries to include type parameters across various methods for improved type safety
This commit is contained in:
Danny Avila 2026-05-07 09:47:40 -04:00 committed by GitHub
parent 1bc2692a15
commit 40a05bbf83
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 243 additions and 194 deletions

View file

@ -53,7 +53,7 @@
"@node-saml/passport-saml": "^5.1.0",
"@smithy/node-http-handler": "^4.4.5",
"ai-tokenizer": "^1.0.6",
"axios": "^1.15.0",
"axios": "^1.16.0",
"bcryptjs": "^2.4.3",
"compression": "^1.8.1",
"connect-redis": "^8.1.0",
@ -65,7 +65,7 @@
"eventsource": "^3.0.2",
"express": "^5.2.1",
"express-mongo-sanitize": "^2.2.0",
"express-rate-limit": "^8.3.0",
"express-rate-limit": "^8.5.1",
"express-session": "^1.18.2",
"express-static-gzip": "^2.2.0",
"file-type": "^21.3.2",
@ -88,7 +88,7 @@
"memorystore": "^1.6.7",
"mime": "^3.0.0",
"module-alias": "^2.2.3",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"multer": "^2.1.1",
"nanoid": "^3.3.7",
"node-fetch": "^2.7.0",

View file

@ -3,7 +3,7 @@
"noImplicitAny": false,
"outDir": "./dist",
"strict": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"esModuleInterop": true,
"target": "es6",
"module": "esnext",

58
package-lock.json generated
View file

@ -68,7 +68,7 @@
"@node-saml/passport-saml": "^5.1.0",
"@smithy/node-http-handler": "^4.4.5",
"ai-tokenizer": "^1.0.6",
"axios": "^1.15.0",
"axios": "^1.16.0",
"bcryptjs": "^2.4.3",
"compression": "^1.8.1",
"connect-redis": "^8.1.0",
@ -80,7 +80,7 @@
"eventsource": "^3.0.2",
"express": "^5.2.1",
"express-mongo-sanitize": "^2.2.0",
"express-rate-limit": "^8.3.0",
"express-rate-limit": "^8.5.1",
"express-session": "^1.18.2",
"express-static-gzip": "^2.2.0",
"file-type": "^21.3.2",
@ -103,7 +103,7 @@
"memorystore": "^1.6.7",
"mime": "^3.0.0",
"module-alias": "^2.2.3",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"multer": "^2.1.1",
"nanoid": "^3.3.7",
"node-fetch": "^2.7.0",
@ -27346,12 +27346,12 @@
}
},
"node_modules/express-rate-limit": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.0.tgz",
"integrity": "sha512-KJzBawY6fB9FiZGdE/0aftepZ91YlaGIrV8vgblRM3J8X+dHx/aiowJWwkx6LIGyuqGiANsjSwwrbb8mifOJ4Q==",
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.1.tgz",
"integrity": "sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==",
"license": "MIT",
"dependencies": {
"ip-address": "10.1.0"
"ip-address": "^10.2.0"
},
"engines": {
"node": ">= 16"
@ -29031,9 +29031,9 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/hono": {
"version": "4.12.14",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz",
"integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==",
"version": "4.12.18",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.18.tgz",
"integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
@ -29525,9 +29525,9 @@
}
},
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"license": "MIT",
"engines": {
"node": ">= 12"
@ -34580,14 +34580,14 @@
}
},
"node_modules/mongodb": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz",
"integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==",
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
"integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==",
"license": "Apache-2.0",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.9",
"bson": "^6.10.3",
"mongodb-connection-string-url": "^3.0.0"
"@mongodb-js/saslprep": "^1.3.0",
"bson": "^6.10.4",
"mongodb-connection-string-url": "^3.0.2"
},
"engines": {
"node": ">=16.20.1"
@ -34598,7 +34598,7 @@
"gcp-metadata": "^5.2.0",
"kerberos": "^2.0.1",
"mongodb-client-encryption": ">=6.0.0 <7",
"snappy": "^7.2.2",
"snappy": "^7.3.2",
"socks": "^2.7.1"
},
"peerDependenciesMeta": {
@ -34755,14 +34755,14 @@
}
},
"node_modules/mongoose": {
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.12.1.tgz",
"integrity": "sha512-UW22y8QFVYmrb36hm8cGncfn4ARc/XsYWQwRTaj0gxtQk1rDuhzDO1eBantS+hTTatfAIS96LlRCJrcNHvW5+Q==",
"version": "8.23.1",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.1.tgz",
"integrity": "sha512-gHSPD8qEwRmiXapK17hEnFWZdcFENMegHTcw5XIIg2+7R8eXQvdwSiMpD/A2oG8tKzFLLHyRXd8/eaDPAVwZgQ==",
"license": "MIT",
"dependencies": {
"bson": "^6.10.3",
"bson": "^6.10.4",
"kareem": "2.6.3",
"mongodb": "~6.14.0",
"mongodb": "~6.20.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@ -44684,7 +44684,7 @@
"@modelcontextprotocol/sdk": "^1.29.0",
"@smithy/node-http-handler": "^4.4.5",
"ai-tokenizer": "^1.0.6",
"axios": "^1.15.0",
"axios": "^1.16.0",
"connect-redis": "^8.1.0",
"eventsource": "^3.0.2",
"express": "^5.1.0",
@ -44703,7 +44703,7 @@
"mammoth": "^1.11.0",
"mathjs": "^15.2.0",
"memorystore": "^1.6.7",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"node-fetch": "2.7.0",
"pdfjs-dist": "^5.4.624",
"rate-limit-redis": "^4.2.0",
@ -46430,7 +46430,7 @@
"version": "0.8.501",
"license": "ISC",
"dependencies": {
"axios": "^1.15.0",
"axios": "^1.16.0",
"dayjs": "^1.11.13",
"js-yaml": "^4.1.1",
"zod": "^3.22.4"
@ -46537,7 +46537,7 @@
"librechat-data-provider": "*",
"lodash": "^4.17.23",
"meilisearch": "^0.38.0",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"nanoid": "^3.3.7",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"

View file

@ -168,7 +168,7 @@
"ajv": "6.14.0"
},
"underscore": "1.13.8",
"hono": "^4.12.4",
"hono": "^4.12.18",
"@hono/node-server": "^1.19.10",
"monaco-editor": {
"dompurify": "3.4.0"

View file

@ -103,7 +103,7 @@
"@modelcontextprotocol/sdk": "^1.29.0",
"@smithy/node-http-handler": "^4.4.5",
"ai-tokenizer": "^1.0.6",
"axios": "^1.15.0",
"axios": "^1.16.0",
"connect-redis": "^8.1.0",
"eventsource": "^3.0.2",
"express": "^5.1.0",
@ -122,7 +122,7 @@
"mammoth": "^1.11.0",
"mathjs": "^15.2.0",
"memorystore": "^1.6.7",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"node-fetch": "2.7.0",
"pdfjs-dist": "^5.4.624",
"rate-limit-redis": "^4.2.0",

View file

@ -23,7 +23,7 @@ export interface Principal {
export interface EnricherDependencies {
aggregateAclEntries: (pipeline: PipelineStage[]) => Promise<Record<string, unknown>[]>;
bulkWriteAclEntries: (
ops: AnyBulkWriteOperation<unknown>[],
ops: AnyBulkWriteOperation[],
options?: Record<string, unknown>,
) => Promise<unknown>;
findRoleByIdentifier: (

View file

@ -15,15 +15,14 @@ jest.mock('@librechat/data-schemas', () => ({
let mongoServer: MongoMemoryServer;
let Balance: mongoose.Model<IBalance>;
const findBalanceByUser = (userId: string) =>
Balance.findOne({ user: userId }).lean() as Promise<IBalance | null>;
const findBalanceByUser = (userId: string) => Balance.findOne({ user: userId }).lean<IBalance>();
const upsertBalanceFields = (userId: string, fields: IBalanceUpdate) =>
Balance.findOneAndUpdate(
{ user: userId },
{ $set: fields },
{ upsert: true, new: true },
).lean() as Promise<IBalance | null>;
).lean<IBalance>();
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();

View file

@ -28,6 +28,17 @@ jest.mock('@librechat/data-schemas', () => ({
let mongoServer: MongoMemoryServer;
let methods: AllMethods;
const mockGetUserPrincipals = () =>
jest.fn<ReturnType<AllMethods['getUserPrincipals']>, Parameters<AllMethods['getUserPrincipals']>>(
methods.getUserPrincipals,
);
const mockHasCapabilityForPrincipals = () =>
jest.fn<
ReturnType<AllMethods['hasCapabilityForPrincipals']>,
Parameters<AllMethods['hasCapabilityForPrincipals']>
>(methods.hasCapabilityForPrincipals);
beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
await mongoose.connect(mongoServer.getUri());
@ -229,7 +240,7 @@ describe('capabilities integration (real MongoDB)', () => {
describe('AsyncLocalStorage per-request caching', () => {
it('caches getUserPrincipals within a single request context', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -247,7 +258,7 @@ describe('capabilities integration (real MongoDB)', () => {
it('caches capability results within a single request context', async () => {
await methods.seedSystemGrants();
const hasCapabilityForPrincipals = jest.fn(methods.hasCapabilityForPrincipals);
const hasCapabilityForPrincipals = mockHasCapabilityForPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals: methods.getUserPrincipals,
@ -262,14 +273,16 @@ describe('capabilities integration (real MongoDB)', () => {
});
const accessAdminCalls = hasCapabilityForPrincipals.mock.calls.filter(
(args) => args[0].capability === SystemCapabilities.ACCESS_ADMIN,
(args) =>
(args[0] as { capability: SystemCapability }).capability ===
SystemCapabilities.ACCESS_ADMIN,
);
expect(accessAdminCalls).toHaveLength(1);
});
it('does NOT share cache across separate request contexts', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -325,7 +338,7 @@ describe('capabilities integration (real MongoDB)', () => {
it('falls through to DB when outside request context (no ALS)', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -339,7 +352,7 @@ describe('capabilities integration (real MongoDB)', () => {
});
it('caches false results correctly (negative caching)', async () => {
const hasCapabilityForPrincipals = jest.fn(methods.hasCapabilityForPrincipals);
const hasCapabilityForPrincipals = mockHasCapabilityForPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals: methods.getUserPrincipals,
@ -354,14 +367,16 @@ describe('capabilities integration (real MongoDB)', () => {
});
const manageUserCalls = hasCapabilityForPrincipals.mock.calls.filter(
(args) => args[0].capability === SystemCapabilities.MANAGE_USERS,
(args) =>
(args[0] as { capability: SystemCapability }).capability ===
SystemCapabilities.MANAGE_USERS,
);
expect(manageUserCalls).toHaveLength(1);
});
it('uses separate principal cache keys for different users in same context', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -378,7 +393,7 @@ describe('capabilities integration (real MongoDB)', () => {
it('uses separate principal cache keys for different tenantIds (same user)', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -465,8 +480,8 @@ describe('capabilities integration (real MongoDB)', () => {
it('every DB call executes (no caching) when ALS context is missing', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const hasCapabilityForPrincipals = jest.fn(methods.hasCapabilityForPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const hasCapabilityForPrincipals = mockHasCapabilityForPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -485,7 +500,7 @@ describe('capabilities integration (real MongoDB)', () => {
it('nested capabilityContextMiddleware creates an independent inner context', async () => {
await methods.seedSystemGrants();
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,
@ -581,7 +596,7 @@ describe('capabilities integration (real MongoDB)', () => {
capability: SystemCapabilities.READ_AGENTS,
});
const getUserPrincipals = jest.fn(methods.getUserPrincipals);
const getUserPrincipals = mockGetUserPrincipals();
const { hasCapability } = generateCapabilityCheck({
getUserPrincipals,

View file

@ -6,7 +6,7 @@
"noImplicitAny": true,
"outDir": "./types",
"target": "es2015",
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"lib": ["es2017", "dom", "ES2021.String", "ES2021.WeakRef"],
"allowJs": true,

View file

@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"jsxImportSource": "react",
"declaration": true,

View file

@ -43,7 +43,7 @@
},
"homepage": "https://librechat.ai",
"dependencies": {
"axios": "^1.15.0",
"axios": "^1.16.0",
"dayjs": "^1.11.13",
"js-yaml": "^4.1.1",
"zod": "^3.22.4"

View file

@ -6,7 +6,7 @@
"": {
"name": "librechat-data-provider/react-query",
"dependencies": {
"axios": "^1.15.0"
"axios": "^1.16.0"
}
},
"node_modules/asynckit": {
@ -16,12 +16,12 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz",
"integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"proxy-from-env": "^2.1.0"
}
@ -120,9 +120,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",

View file

@ -5,6 +5,6 @@
"module": "./index.es.js",
"types": "../dist/types/react-query/index.d.ts",
"dependencies": {
"axios": "^1.15.0"
"axios": "^1.16.0"
}
}

View file

@ -6,7 +6,7 @@
"noImplicitAny": true,
"outDir": "./types",
"target": "es5",
"moduleResolution": "node",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"lib": ["es2017", "dom", "ES2021.String"],
"allowJs": true,

View file

@ -69,7 +69,7 @@
"librechat-data-provider": "*",
"lodash": "^4.17.23",
"meilisearch": "^0.38.0",
"mongoose": "^8.12.1",
"mongoose": "^8.23.1",
"nanoid": "^3.3.7",
"winston": "^3.17.0",
"winston-daily-rotate-file": "^5.0.0"

View file

@ -11,7 +11,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
*/
async function findRoleById(roleId: string | Types.ObjectId): Promise<IAccessRole | null> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
return await AccessRole.findById(roleId).lean();
return await AccessRole.findById(roleId).lean<IAccessRole>();
}
/**
@ -23,7 +23,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
accessRoleId: string | Types.ObjectId,
): Promise<IAccessRole | null> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
return await AccessRole.findOne({ accessRoleId }).lean();
return await AccessRole.findOne({ accessRoleId }).lean<IAccessRole>();
}
/**
@ -33,7 +33,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
*/
async function findRolesByResourceType(resourceType: string): Promise<IAccessRole[]> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
return await AccessRole.find({ resourceType }).lean();
return await AccessRole.find({ resourceType }).lean<IAccessRole[]>();
}
/**
@ -47,7 +47,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
permBits: PermissionBits | RoleBits,
): Promise<IAccessRole | null> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
return await AccessRole.findOne({ resourceType, permBits }).lean();
return await AccessRole.findOne({ resourceType, permBits }).lean<IAccessRole>();
}
/**
@ -75,7 +75,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
{ accessRoleId },
{ $set: updateData },
{ new: true },
).lean();
).lean<IAccessRole>();
}
/**
@ -94,7 +94,7 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
*/
async function getAllRoles(): Promise<IAccessRole[]> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
return await AccessRole.find().lean();
return await AccessRole.find().lean<IAccessRole[]>();
}
/**
@ -218,9 +218,11 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
{ accessRoleId: role.accessRoleId },
{ $setOnInsert: role },
{ upsert: true, new: true },
).lean();
).lean<IAccessRole>();
result[role.accessRoleId] = upsertedRole;
if (upsertedRole) {
result[role.accessRoleId] = upsertedRole;
}
}
return result;
@ -237,13 +239,15 @@ export function createAccessRoleMethods(mongoose: typeof import('mongoose')) {
permBits: PermissionBits | RoleBits,
): Promise<IAccessRole | null> {
const AccessRole = mongoose.models.AccessRole as Model<IAccessRole>;
const exactMatch = await AccessRole.findOne({ resourceType, permBits }).lean();
const exactMatch = await AccessRole.findOne({ resourceType, permBits }).lean<IAccessRole>();
if (exactMatch) {
return exactMatch;
}
/** If no exact match, the closest role without exceeding permissions */
const roles = await AccessRole.find({ resourceType }).sort({ permBits: -1 }).lean();
const roles = await AccessRole.find({ resourceType })
.sort({ permBits: -1 })
.lean<IAccessRole[]>();
return roles.find((role) => (role.permBits & permBits) === role.permBits) || null;
}

View file

@ -93,7 +93,7 @@ export function createAclEntryMethods(mongoose: typeof import('mongoose')) {
if (resourceType) {
query.resourceType = resourceType;
}
return await AclEntry.find(query).lean();
return await AclEntry.find(query).lean<IAclEntry[]>();
}
/**
@ -107,7 +107,7 @@ export function createAclEntryMethods(mongoose: typeof import('mongoose')) {
resourceId: string | Types.ObjectId,
): Promise<IAclEntry[]> {
const AclEntry = mongoose.models.AclEntry as Model<IAclEntry>;
return await AclEntry.find({ resourceType, resourceId }).lean();
return await AclEntry.find({ resourceType, resourceId }).lean<IAclEntry[]>();
}
/**
@ -132,7 +132,7 @@ export function createAclEntryMethods(mongoose: typeof import('mongoose')) {
$or: principalsQuery,
resourceType,
resourceId,
}).lean();
}).lean<IAclEntry[]>();
}
/**

View file

@ -14,11 +14,7 @@ export function createActionMethods(mongoose: typeof import('mongoose')) {
): Promise<IAction | null> {
const Action = mongoose.models.Action as Model<IAction>;
const options = { new: true, upsert: true };
return (await Action.findOneAndUpdate(
searchParams,
updateData,
options,
).lean()) as IAction | null;
return await Action.findOneAndUpdate(searchParams, updateData, options).lean<IAction>();
}
/**
@ -29,7 +25,7 @@ export function createActionMethods(mongoose: typeof import('mongoose')) {
includeSensitive = false,
): Promise<IAction[]> {
const Action = mongoose.models.Action as Model<IAction>;
const actions = (await Action.find(searchParams).lean()) as IAction[];
const actions = await Action.find(searchParams).lean<IAction[]>();
if (!includeSensitive) {
for (let i = 0; i < actions.length; i++) {
@ -54,7 +50,7 @@ export function createActionMethods(mongoose: typeof import('mongoose')) {
*/
async function deleteAction(searchParams: FilterQuery<IAction>): Promise<IAction | null> {
const Action = mongoose.models.Action as Model<IAction>;
return (await Action.findOneAndDelete(searchParams).lean()) as IAction | null;
return await Action.findOneAndDelete(searchParams).lean<IAction>();
}
/**

View file

@ -271,7 +271,7 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
*/
async function getAgent(searchParameter: FilterQuery<IAgent>): Promise<IAgent | null> {
const Agent = mongoose.models.Agent as Model<IAgent>;
return (await Agent.findOne(searchParameter).lean()) as IAgent | null;
return await Agent.findOne(searchParameter).lean<IAgent>();
}
/**
@ -279,7 +279,7 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
*/
async function getAgents(searchParameter: FilterQuery<IAgent>): Promise<IAgent[]> {
const Agent = mongoose.models.Agent as Model<IAgent>;
return (await Agent.find(searchParameter).lean()) as IAgent[];
return await Agent.find(searchParameter).lean<IAgent[]>();
}
/**
@ -780,9 +780,13 @@ export function createAgentMethods(mongoose: typeof import('mongoose'), deps: Ag
delete revertToVersion.author;
delete revertToVersion.updatedBy;
return (await Agent.findOneAndUpdate(searchParameter, revertToVersion, {
const revertedAgent = await Agent.findOneAndUpdate(searchParameter, revertToVersion, {
new: true,
}).lean()) as IAgent;
}).lean<IAgent>();
if (!revertedAgent) {
throw new Error('Agent not found');
}
return revertedAgent;
}
/**

View file

@ -9,7 +9,9 @@ export function createAgentCategoryMethods(mongoose: typeof import('mongoose'))
*/
async function getActiveCategories(): Promise<IAgentCategory[]> {
const AgentCategory = mongoose.models.AgentCategory as Model<IAgentCategory>;
return await AgentCategory.find({ isActive: true }).sort({ order: 1, label: 1 }).lean();
return await AgentCategory.find({ isActive: true })
.sort({ order: 1, label: 1 })
.lean<IAgentCategory[]>();
}
/**
@ -85,7 +87,7 @@ export function createAgentCategoryMethods(mongoose: typeof import('mongoose'))
*/
async function findCategoryByValue(value: string): Promise<IAgentCategory | null> {
const AgentCategory = mongoose.models.AgentCategory as Model<IAgentCategory>;
return await AgentCategory.findOne({ value }).lean();
return await AgentCategory.findOne({ value }).lean<IAgentCategory>();
}
/**
@ -114,7 +116,7 @@ export function createAgentCategoryMethods(mongoose: typeof import('mongoose'))
{ value },
{ $set: updateData },
{ new: true, runValidators: true },
).lean();
).lean<IAgentCategory>();
}
/**
@ -135,7 +137,7 @@ export function createAgentCategoryMethods(mongoose: typeof import('mongoose'))
*/
async function findCategoryById(id: string | Types.ObjectId): Promise<IAgentCategory | null> {
const AgentCategory = mongoose.models.AgentCategory as Model<IAgentCategory>;
return await AgentCategory.findById(id).lean();
return await AgentCategory.findById(id).lean<IAgentCategory>();
}
/**
@ -144,7 +146,7 @@ export function createAgentCategoryMethods(mongoose: typeof import('mongoose'))
*/
async function getAllCategories(): Promise<IAgentCategory[]> {
const AgentCategory = mongoose.models.AgentCategory as Model<IAgentCategory>;
return await AgentCategory.find({}).sort({ order: 1, label: 1 }).lean();
return await AgentCategory.find({}).sort({ order: 1, label: 1 }).lean<IAgentCategory[]>();
}
/**

View file

@ -12,11 +12,7 @@ export function createAssistantMethods(mongoose: typeof import('mongoose')) {
): Promise<IAssistant | null> {
const Assistant = mongoose.models.Assistant as Model<IAssistant>;
const options = { new: true, upsert: true };
return (await Assistant.findOneAndUpdate(
searchParams,
updateData,
options,
).lean()) as IAssistant | null;
return await Assistant.findOneAndUpdate(searchParams, updateData, options).lean<IAssistant>();
}
/**
@ -24,7 +20,7 @@ export function createAssistantMethods(mongoose: typeof import('mongoose')) {
*/
async function getAssistant(searchParams: FilterQuery<IAssistant>): Promise<IAssistant | null> {
const Assistant = mongoose.models.Assistant as Model<IAssistant>;
return (await Assistant.findOne(searchParams).lean()) as IAssistant | null;
return await Assistant.findOne(searchParams).lean<IAssistant>();
}
/**
@ -37,7 +33,7 @@ export function createAssistantMethods(mongoose: typeof import('mongoose')) {
const Assistant = mongoose.models.Assistant as Model<IAssistant>;
const query = Assistant.find(searchParams);
return (await (select ? query.select(select) : query).lean()) as IAssistant[];
return await (select ? query.select(select) : query).lean<IAssistant[]>();
}
/**

View file

@ -22,7 +22,7 @@ export function createConfigMethods(mongoose: typeof import('mongoose')) {
}
return await Config.findOne(filter)
.session(session ?? null)
.lean();
.lean<IConfig>();
}
async function listAllConfigs(
@ -37,7 +37,7 @@ export function createConfigMethods(mongoose: typeof import('mongoose')) {
return await Config.find(where)
.sort({ priority: 1 })
.session(session ?? null)
.lean();
.lean<IConfig[]>();
}
async function getApplicableConfigs(
@ -70,7 +70,7 @@ export function createConfigMethods(mongoose: typeof import('mongoose')) {
})
.sort({ priority: 1 })
.session(session ?? null)
.lean();
.lean<IConfig[]>();
}
async function upsertConfig(

View file

@ -680,7 +680,7 @@ describe('Conversation Operations', () => {
createdAt,
updatedAt,
});
return Conversation.findOne({ conversationId }).lean();
return Conversation.findOne({ conversationId }).lean<IConversation>();
};
it('should not skip conversations at page boundaries', async () => {

View file

@ -71,7 +71,10 @@ export function createConversationMethods(
async function searchConversation(conversationId: string) {
try {
const Conversation = mongoose.models.Conversation as Model<IConversation>;
return await Conversation.findOne({ conversationId }, 'conversationId user').lean();
return await Conversation.findOne(
{ conversationId },
'conversationId user',
).lean<IConversation>();
} catch (error) {
logger.error('[searchConversation] Error searching conversation', error);
throw new Error('Error searching conversation');
@ -84,7 +87,7 @@ export function createConversationMethods(
async function getConvo(user: string, conversationId: string) {
try {
const Conversation = mongoose.models.Conversation as Model<IConversation>;
return await Conversation.findOne({ user, conversationId }).lean();
return await Conversation.findOne({ user, conversationId }).lean<IConversation>();
} catch (error) {
logger.error('[getConvo] Error getting single conversation', error);
throw new Error('Error getting single conversation');
@ -130,8 +133,7 @@ export function createConversationMethods(
try {
const Conversation = mongoose.models.Conversation as Model<IConversation>;
return (
((await Conversation.findOne({ conversationId }, 'files').lean()) as IConversation | null)
?.files ?? []
(await Conversation.findOne({ conversationId }, 'files').lean<IConversation>())?.files ?? []
);
} catch (error) {
logger.error('[getConvoFiles] Error getting conversation files', error);
@ -381,16 +383,21 @@ export function createConversationMethods(
)
.sort(sortObj)
.limit(limit + 1)
.lean();
.lean<IConversation[]>();
let nextCursor: string | null = null;
if (convos.length > limit) {
convos.pop();
const lastReturned = convos[convos.length - 1] as Record<string, unknown>;
const primaryValue = lastReturned[finalSortBy];
const lastReturned = convos[convos.length - 1];
let primaryValue: string | Date | undefined = lastReturned.updatedAt;
if (finalSortBy === 'title') {
primaryValue = lastReturned.title;
} else if (finalSortBy === 'createdAt') {
primaryValue = lastReturned.createdAt;
}
const primaryStr =
finalSortBy === 'title' ? primaryValue : (primaryValue as Date).toISOString();
const secondaryStr = (lastReturned.updatedAt as Date).toISOString();
finalSortBy === 'title' ? primaryValue : new Date(primaryValue ?? 0).toISOString();
const secondaryStr = new Date(lastReturned.updatedAt ?? 0).toISOString();
const composite = { primary: primaryStr, secondary: secondaryStr };
nextCursor = Buffer.from(JSON.stringify(composite)).toString('base64');
}
@ -423,7 +430,7 @@ export function createConversationMethods(
user,
conversationId: { $in: conversationIds },
$or: [{ expiredAt: { $exists: false } }, { expiredAt: null }],
}).lean();
}).lean<IConversation[]>();
results.sort(
(a, b) => new Date(b.updatedAt ?? 0).getTime() - new Date(a.updatedAt ?? 0).getTime(),

View file

@ -17,7 +17,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
options: Record<string, unknown> = {},
): Promise<IMongoFile | null> {
const File = mongoose.models.File as Model<IMongoFile>;
return File.findOne({ file_id, ...options }).lean();
return File.findOne({ file_id, ...options }).lean<IMongoFile>();
}
/** Select fields for query projection - 0 to exclude, 1 to include */
@ -44,7 +44,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
} else {
query.select({ text: 0 });
}
return await query.sort(sortOptions).lean();
return await query.sort(sortOptions).lean<IMongoFile[]>();
}
/**
@ -198,13 +198,13 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
},
{ $setOnInsert: insertData },
{ upsert: true, new: true },
).lean();
).lean<IMongoFile>();
if (!result) {
throw new Error(
`[claimCodeFile] Failed to claim file "${data.filename}" for conversation ${data.conversationId}`,
);
}
return result as IMongoFile;
return result;
}
/**
@ -230,7 +230,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
return File.findOneAndUpdate({ file_id: data.file_id }, fileData, {
new: true,
upsert: true,
}).lean();
}).lean<IMongoFile>();
}
/**
@ -263,7 +263,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
const query: FilterQuery<IMongoFile> = extraFilter ? { file_id, ...extraFilter } : { file_id };
return File.findOneAndUpdate(query, updateOperation, {
new: true,
}).lean();
}).lean<IMongoFile>();
}
/**
@ -283,7 +283,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
};
return File.findOneAndUpdate({ file_id }, updateOperation, {
new: true,
}).lean();
}).lean<IMongoFile>();
}
/**
@ -293,7 +293,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
*/
async function deleteFile(file_id: string): Promise<IMongoFile | null> {
const File = mongoose.models.File as Model<IMongoFile>;
return File.findOneAndDelete({ file_id }).lean();
return File.findOneAndDelete({ file_id }).lean<IMongoFile>();
}
/**
@ -303,7 +303,7 @@ export function createFileMethods(mongoose: typeof import('mongoose')) {
*/
async function deleteFileByFilter(filter: FilterQuery<IMongoFile>): Promise<IMongoFile | null> {
const File = mongoose.models.File as Model<IMongoFile>;
return File.findOneAndDelete(filter).lean();
return File.findOneAndDelete(filter).lean<IMongoFile>();
}
/**

View file

@ -58,7 +58,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
serverName: { $regex: `^${escapedBaseName}(-\\d+)?$` },
})
.select('serverName')
.lean();
.lean<Array<{ serverName: string }>>();
if (existing.length === 0) {
return baseName;
@ -139,7 +139,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
*/
async function findMCPServerByServerName(serverName: string): Promise<MCPServerDocument | null> {
const MCPServer = mongoose.models.MCPServer as Model<MCPServerDocument>;
return await MCPServer.findOne({ serverName }).lean();
return await MCPServer.findOne({ serverName }).lean<MCPServerDocument>();
}
/**
@ -151,7 +151,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
_id: string | Types.ObjectId,
): Promise<MCPServerDocument | null> {
const MCPServer = mongoose.models.MCPServer as Model<MCPServerDocument>;
return await MCPServer.findById(_id).lean();
return await MCPServer.findById(_id).lean<MCPServerDocument>();
}
/**
@ -163,7 +163,9 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
authorId: string | Types.ObjectId,
): Promise<MCPServerDocument[]> {
const MCPServer = mongoose.models.MCPServer as Model<MCPServerDocument>;
return await MCPServer.find({ author: authorId }).sort({ updatedAt: -1 }).lean();
return await MCPServer.find({ author: authorId })
.sort({ updatedAt: -1 })
.lean<MCPServerDocument[]>();
}
/**
@ -229,7 +231,9 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
if (normalizedLimit === null) {
// No pagination - return all matching servers
const servers = await MCPServer.find(baseQuery).sort({ updatedAt: -1, _id: 1 }).lean();
const servers = await MCPServer.find(baseQuery)
.sort({ updatedAt: -1, _id: 1 })
.lean<MCPServerDocument[]>();
return {
data: servers,
@ -242,7 +246,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
const servers = await MCPServer.find(baseQuery)
.sort({ updatedAt: -1, _id: 1 })
.limit(normalizedLimit + 1)
.lean();
.lean<MCPServerDocument[]>();
const hasMore = servers.length > normalizedLimit;
const data = hasMore ? servers.slice(0, normalizedLimit) : servers;
@ -280,7 +284,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
{ serverName },
{ $set: updateData },
{ new: true, runValidators: true },
).lean();
).lean<MCPServerDocument>();
}
/**
@ -290,7 +294,7 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
*/
async function deleteMCPServer(serverName: string): Promise<MCPServerDocument | null> {
const MCPServer = mongoose.models.MCPServer as Model<MCPServerDocument>;
return await MCPServer.findOneAndDelete({ serverName }).lean();
return await MCPServer.findOneAndDelete({ serverName }).lean<MCPServerDocument>();
}
/**
@ -305,7 +309,9 @@ export function createMCPServerMethods(mongoose: typeof import('mongoose')) {
return { data: [] };
}
const MCPServer = mongoose.models.MCPServer as Model<MCPServerDocument>;
const servers = await MCPServer.find({ serverName: { $in: names } }).lean();
const servers = await MCPServer.find({ serverName: { $in: names } }).lean<
MCPServerDocument[]
>();
return { data: servers };
}

View file

@ -626,7 +626,7 @@ describe('Message Operations', () => {
createdAt,
updatedAt: createdAt,
});
return Message.findOne({ messageId }).lean();
return Message.findOne({ messageId }).lean<IMessage>();
};
/**

View file

@ -276,7 +276,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
) {
try {
const Message = mongoose.models.Message as Model<IMessage>;
const message = await Message.findOne({ messageId, user: userId }).lean();
const message = await Message.findOne({ messageId, user: userId }).lean<IMessage>();
if (message) {
const query = Message.find({ conversationId, user: userId });
@ -298,10 +298,10 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
try {
const Message = mongoose.models.Message as Model<IMessage>;
if (select) {
return await Message.find(filter).select(select).sort({ createdAt: 1 }).lean();
return await Message.find(filter).select(select).sort({ createdAt: 1 }).lean<IMessage[]>();
}
return await Message.find(filter).sort({ createdAt: 1 }).lean();
return await Message.find(filter).sort({ createdAt: 1 }).lean<IMessage[]>();
} catch (err) {
logger.error('Error getting messages:', err);
throw err;
@ -314,7 +314,7 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
async function getMessage({ user, messageId }: { user: string; messageId: string }) {
try {
const Message = mongoose.models.Message as Model<IMessage>;
return await Message.findOne({ user, messageId }).lean();
return await Message.findOne({ user, messageId }).lean<IMessage>();
} catch (err) {
logger.error('Error getting message:', err);
throw err;
@ -355,13 +355,15 @@ export function createMessageMethods(mongoose: typeof import('mongoose')): Messa
const messages = await Message.find(queryFilter)
.sort({ [sortField]: sortOrder })
.limit(limit + 1)
.lean();
.lean<IMessage[]>();
let nextCursor: string | null = null;
if (messages.length > limit) {
messages.pop();
const last = messages[messages.length - 1] as Record<string, unknown>;
nextCursor = String(last[sortField] ?? '');
const last = messages[messages.length - 1];
const cursorValue =
sortField === 'createdAt' ? last.createdAt : last[sortField as keyof IMessage];
nextCursor = String(cursorValue ?? '');
}
return { messages, nextCursor };
}

View file

@ -23,7 +23,7 @@ export function createPluginAuthMethods(mongoose: typeof import('mongoose')) {
userId,
authField,
...(pluginKey && { pluginKey }),
}).lean();
}).lean<IPluginAuth>();
} catch (error) {
throw new Error(
`Failed to find plugin auth: ${error instanceof Error ? error.message : 'Unknown error'}`,
@ -47,7 +47,7 @@ export function createPluginAuthMethods(mongoose: typeof import('mongoose')) {
return await PluginAuth.find({
userId,
pluginKey: { $in: pluginKeys },
}).lean();
}).lean<IPluginAuth[]>();
} catch (error) {
throw new Error(
`Failed to find plugin auths: ${error instanceof Error ? error.message : 'Unknown error'}`,
@ -66,14 +66,18 @@ export function createPluginAuthMethods(mongoose: typeof import('mongoose')) {
}: UpdatePluginAuthParams): Promise<IPluginAuth> {
try {
const PluginAuth: Model<IPluginAuth> = mongoose.models.PluginAuth;
const existingAuth = await PluginAuth.findOne({ userId, pluginKey, authField }).lean();
const existingAuth = await PluginAuth.findOne({
userId,
pluginKey,
authField,
}).lean<IPluginAuth>();
if (existingAuth) {
return await PluginAuth.findOneAndUpdate(
{ userId, pluginKey, authField },
{ $set: { value } },
{ new: true, upsert: true },
).lean();
).lean<IPluginAuth>();
} else {
const newPluginAuth = await new PluginAuth({
userId,

View file

@ -146,7 +146,13 @@ export function createRoleMethods(mongoose: typeof import('mongoose'), deps: Rol
const targetName = updates.name ?? roleName;
throw new RoleConflictError(`Role "${targetName}" already exists`);
}
throw new Error(`Failed to update role: ${(error as Error).message}`, { cause: error });
const updateError = new Error(
`Failed to update role: ${(error as Error).message}`,
) as Error & {
cause?: unknown;
};
updateError.cause = error;
throw updateError;
}
}
@ -487,7 +493,7 @@ export function createRoleMethods(mongoose: typeof import('mongoose'), deps: Rol
.sort({ _id: 1 })
.skip(offset)
.limit(limit)
.lean();
.lean<IUser[]>();
}
async function countUsersByRole(roleName: string): Promise<number> {

View file

@ -278,7 +278,7 @@ export function createSystemGrantMethods(mongoose: typeof import('mongoose')) {
...tenantCondition(tenantId),
};
return await SystemGrant.find(filter).lean();
return await SystemGrant.find(filter).lean<ISystemGrant[]>();
}
const GRANTS_DEFAULT_LIMIT = 50;
@ -302,7 +302,7 @@ export function createSystemGrantMethods(mongoose: typeof import('mongoose')) {
.sort({ principalType: 1, capability: 1 })
.skip(offset)
.limit(limit)
.lean();
.lean<ISystemGrant[]>();
}
async function countGrants(options?: {
@ -346,7 +346,7 @@ export function createSystemGrantMethods(mongoose: typeof import('mongoose')) {
...tenantCondition(tenantId),
};
return await SystemGrant.find(filter).lean();
return await SystemGrant.find(filter).lean<ISystemGrant[]>();
}
/**

View file

@ -188,9 +188,9 @@ export function createTransactionMethods(
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
let currentBalanceDoc;
let currentBalanceDoc: IBalance | null;
try {
currentBalanceDoc = await Balance.findOne({ user }).lean();
currentBalanceDoc = await Balance.findOne({ user }).lean<IBalance>();
const currentCredits = currentBalanceDoc ? currentBalanceDoc.tokenCredits : 0;
const potentialNewCredits = currentCredits + incrementValue;
const newCredits = Math.max(0, potentialNewCredits);
@ -208,7 +208,7 @@ export function createTransactionMethods(
{ user, tokenCredits: currentCredits },
updatePayload,
{ new: true },
).lean();
).lean<IBalance>();
if (updatedBalance) {
return updatedBalance;
@ -219,7 +219,7 @@ export function createTransactionMethods(
updatedBalance = await Balance.findOneAndUpdate({ user }, updatePayload, {
upsert: true,
new: true,
}).lean();
}).lean<IBalance>();
if (updatedBalance) {
return updatedBalance;
@ -384,7 +384,7 @@ export function createTransactionMethods(
/** Retrieves a user's balance record. */
async function findBalanceByUser(user: string): Promise<IBalance | null> {
const Balance = mongoose.models.Balance as Model<IBalance>;
return Balance.findOne({ user }).lean();
return Balance.findOne({ user }).lean<IBalance>();
}
/** Upserts balance fields for a user. */
@ -393,7 +393,11 @@ export function createTransactionMethods(
fields: IBalanceUpdate,
): Promise<IBalance | null> {
const Balance = mongoose.models.Balance as Model<IBalance>;
return Balance.findOneAndUpdate({ user }, { $set: fields }, { upsert: true, new: true }).lean();
return Balance.findOneAndUpdate(
{ user },
{ $set: fields },
{ upsert: true, new: true },
).lean<IBalance>();
}
/** Deletes transactions matching a filter. */

View file

@ -42,7 +42,7 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
if (fieldsToSelect) {
query.select(fieldsToSelect);
}
return await query.lean();
return await query.lean<IUser>();
}
async function findUsers(
@ -65,7 +65,7 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
if (options?.limit != null && options.limit > 0) {
query.limit(options.limit);
}
return (await query.lean()) as IUser[];
return await query.lean<IUser[]>();
}
/**
@ -148,10 +148,10 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
$set: updateData,
$unset: { expiresAt: '' }, // Remove the expiresAt field to prevent TTL
};
return (await User.findByIdAndUpdate(userId, updateOperation, {
return await User.findByIdAndUpdate(userId, updateOperation, {
new: true,
runValidators: true,
}).lean()) as IUser | null;
}).lean<IUser>();
}
/**
@ -166,7 +166,7 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
if (fieldsToSelect) {
query.select(fieldsToSelect);
}
return (await query.lean()) as IUser | null;
return await query.lean<IUser>();
}
/**
@ -233,10 +233,10 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
},
};
return (await User.findByIdAndUpdate(userId, updateOperation, {
return await User.findByIdAndUpdate(userId, updateOperation, {
new: true,
runValidators: true,
}).lean()) as IUser | null;
}).lean<IUser>();
}
/**
@ -270,14 +270,16 @@ export function createUserMethods(mongoose: typeof import('mongoose')) {
query.select(fieldsToSelect);
}
const users = await query.lean();
const users = await query.lean<IUser[]>();
// Score results by relevance
const exactRegex = new RegExp(`^${searchPattern.trim()}$`, 'i');
const startsWithPattern = searchPattern.trim().toLowerCase();
const scoredUsers = users.map((user) => {
const searchableFields = [user.name, user.email, user.username].filter(Boolean);
const searchableFields = [user.name, user.email, user.username].filter(
(field): field is string => typeof field === 'string' && field.length > 0,
);
let maxScore = 0;
for (const field of searchableFields) {

View file

@ -23,7 +23,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
return await query.lean();
return await query.lean<IGroup>();
}
/**
@ -45,7 +45,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
return await query.lean();
return await query.lean<IGroup>();
}
/**
@ -68,7 +68,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
return await query.lean();
return await query.lean<IGroup[]>();
}
/**
@ -99,7 +99,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
dbQuery.session(session);
}
return await dbQuery.lean();
return await dbQuery.lean<IGroup[]>();
}
/**
@ -119,7 +119,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
userQuery.session(session);
}
const user = (await userQuery.lean()) as { idOnTheSource?: string } | null;
const user = await userQuery.lean<{ idOnTheSource?: string }>();
if (!user) {
return [];
@ -131,7 +131,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
return await query.lean();
return await query.lean<IGroup[]>();
}
/**
@ -190,10 +190,10 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
const options = { new: true, ...(session ? { session } : {}) };
const user = (await User.findById(userId, 'idOnTheSource', options).lean()) as {
const user = await User.findById(userId, 'idOnTheSource', options).lean<{
idOnTheSource?: string;
_id: Types.ObjectId;
} | null;
}>();
if (!user) {
throw new Error(`User not found: ${userId}`);
}
@ -203,7 +203,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
groupId,
{ $addToSet: { memberIds: userIdOnTheSource } },
options,
).lean();
).lean<IGroup>();
return { user: user as IUser, group: updatedGroup };
}
@ -228,10 +228,10 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
const options = { new: true, ...(session ? { session } : {}) };
const user = (await User.findById(userId, 'idOnTheSource', options).lean()) as {
const user = await User.findById(userId, 'idOnTheSource', options).lean<{
idOnTheSource?: string;
_id: Types.ObjectId;
} | null;
}>();
if (!user) {
throw new Error(`User not found: ${userId}`);
}
@ -241,7 +241,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
groupId,
{ $pullAll: { memberIds: [userIdOnTheSource] } },
options,
).lean();
).lean<IGroup>();
return { user: user as IUser, group: updatedGroup };
}
@ -305,7 +305,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
const user = await query.lean();
const user = await query.lean<IUser>();
userRole = user?.role;
}
@ -349,7 +349,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
const user = (await query.lean()) as { idOnTheSource?: string; _id: Types.ObjectId } | null;
const user = await query.lean<{ idOnTheSource?: string; _id: Types.ObjectId }>();
if (!user) {
throw new Error(`User not found: ${userId}`);
@ -396,10 +396,12 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
groupsQuery.session(session);
}
const existingGroups = (await groupsQuery.lean()) as Array<{
_id: Types.ObjectId;
idOnTheSource?: string;
}>;
const existingGroups = await groupsQuery.lean<
Array<{
_id: Types.ObjectId;
idOnTheSource?: string;
}>
>();
for (const group of existingGroups) {
if (group.idOnTheSource && !entraIdMap.has(group.idOnTheSource)) {
@ -414,7 +416,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
userQuery.session(session);
}
const updatedUser = await userQuery.lean();
const updatedUser = await userQuery.lean<IUser>();
if (!updatedUser) {
throw new Error(`User not found after update: ${userId}`);
@ -568,7 +570,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
}
promises.push(
userQuery.lean().then((users) =>
userQuery.lean<IUser[]>().then((users) =>
users.map((user) => {
const userWithId = user as IUser & { idOnTheSource?: string };
return transformUserToTPrincipalSearchResult({
@ -607,7 +609,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
}
promises.push(
roleQuery.lean().then((roles) =>
roleQuery.lean<Array<{ name: string }>>().then((roles) =>
roles.map((role) => ({
/** Role name as ID */
id: role.name,
@ -650,7 +652,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
if (session) {
query.session(session);
}
return query.lean();
return query.lean<IGroup>();
}
/**
@ -665,7 +667,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
): Promise<IGroup | null> {
const Group = mongoose.models.Group as Model<IGroup>;
const options = { new: true, ...(session ? { session } : {}) };
return Group.findByIdAndUpdate(groupId, { $set: data }, options).lean();
return Group.findByIdAndUpdate(groupId, { $set: data }, options).lean<IGroup>();
}
/**
@ -722,7 +724,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
.skip(offset)
.limit(limit)
.session(session ?? null)
.lean();
.lean<IGroup[]>();
}
/**
@ -750,7 +752,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
): Promise<IGroup | null> {
const Group = mongoose.models.Group as Model<IGroup>;
const options = session ? { session } : {};
return await Group.findByIdAndDelete(groupId, options).lean();
return await Group.findByIdAndDelete(groupId, options).lean<IGroup>();
}
/**
@ -771,7 +773,7 @@ export function createUserGroupMethods(mongoose: typeof import('mongoose')) {
groupId,
{ $pull: { memberIds: memberId } },
options,
).lean();
).lean<IGroup>();
}
return {

View file

@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ES2019",
"module": "ESNext",
"moduleResolution": "node",
"moduleResolution": "bundler",
"declaration": false,
"noEmit": true,
"strict": true,