chore: fix various eslint issues #1176

Merged
Sainan merged 1 commits from eslint-fix into main 2025-03-15 03:24:39 -07:00
16 changed files with 111 additions and 75 deletions

View File

@ -38,7 +38,7 @@ export interface IQuestKeyReward {
Duration: number;
CouponSku: number;
Syndicate: string;
Milestones: any[];
//Milestones: any[];
ChooseSetIndex: number;
NewSystemReward: boolean;
_id: IOid;

View File

@ -258,6 +258,7 @@ const resourceGetParent = (resourceName: string): string | undefined => {
if (resourceName in ExportResources) {
return ExportResources[resourceName].parentName;
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
return ExportVirtuals[resourceName]?.parentName;
};

View File

@ -2,12 +2,13 @@ import { RequestHandler } from "express";
import { getSessionByID } from "@/src/managers/sessionManager";
import { logger } from "@/src/utils/logger";
const joinSessionController: RequestHandler = (_req, res) => {
const reqBody = JSON.parse(String(_req.body));
export const joinSessionController: RequestHandler = (req, res) => {
const reqBody = JSON.parse(String(req.body)) as IJoinSessionRequest;
logger.debug(`JoinSession Request`, { reqBody });
const req = JSON.parse(String(_req.body));
const session = getSessionByID(req.sessionIds[0] as string);
const session = getSessionByID(reqBody.sessionIds[0]);
res.json({ rewardSeed: session?.rewardSeed, sessionId: { $oid: session?.sessionId } });
};
export { joinSessionController };
interface IJoinSessionRequest {
sessionIds: string[];
}

View File

@ -44,7 +44,7 @@ function getSessionByID(sessionId: string): ISession | undefined {
return sessions.find(session => session.sessionId === sessionId);
}
function getSession(sessionIdOrRequest: string | IFindSessionRequest): any[] {
function getSession(sessionIdOrRequest: string | IFindSessionRequest): { createdBy: string; id: string }[] {
if (typeof sessionIdOrRequest === "string") {
const session = sessions.find(session => session.sessionId === sessionIdOrRequest);
if (session) {
@ -107,8 +107,7 @@ function updateSession(sessionId: string, sessionData: string): boolean {
const session = sessions.find(session => session.sessionId === sessionId);
if (!session) return false;
try {
const updatedData = JSON.parse(sessionData);
Object.assign(session, updatedData);
Object.assign(session, JSON.parse(sessionData));
return true;
} catch (error) {
console.error("Invalid JSON string for session update.");

View File

@ -1,7 +1,7 @@
import { IOid } from "@/src/types/commonTypes";
import { IEquipmentSelection } from "@/src/types/inventoryTypes/commonInventoryTypes";
import { ILoadoutConfigDatabase, ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { Model, Schema, Types, model } from "mongoose";
import { Document, Model, Schema, Types, model } from "mongoose";
const oidSchema = new Schema<IOid>(
{
@ -97,3 +97,15 @@ type loadoutDocumentProps = {
type loadoutModelType = Model<ILoadoutDatabase, {}, loadoutDocumentProps>;
export const Loadout = model<ILoadoutDatabase, loadoutModelType>("Loadout", loadoutSchema);
// eslint-disable-next-line @typescript-eslint/ban-types
export type TLoadoutDatabaseDocument = Document<unknown, {}, ILoadoutDatabase> &
Omit<
ILoadoutDatabase & {
_id: Types.ObjectId;
} & {
__v: number;
},
keyof loadoutDocumentProps
> &
loadoutDocumentProps;

View File

@ -1,4 +1,4 @@
import { Schema, model } from "mongoose";
import { Document, Schema, Types, model } from "mongoose";
import { IShipDatabase } from "../types/shipTypes";
import { toOid } from "@/src/helpers/inventoryHelpers";
import { colorSchema } from "@/src/models/inventoryModels/inventoryModel";
@ -47,3 +47,11 @@ shipSchema.set("toObject", {
});
export const Ship = model("Ships", shipSchema);
// eslint-disable-next-line @typescript-eslint/ban-types
export type TShipDatabaseDocument = Document<unknown, {}, IShipDatabase> &
IShipDatabase & {
_id: Types.ObjectId;
} & {
__v: number;
};

View File

@ -4,7 +4,7 @@ import { IEnemy, IMission, IScan, ITutorial, IAbility, IWeapon, IStatsDatabase,
const abilitySchema = new Schema<IAbility>(
{
type: { type: String, required: true },
used: Number
used: { type: Number, required: true }
},
{ _id: false }
);
@ -32,7 +32,7 @@ const missionSchema = new Schema<IMission>(
const scanSchema = new Schema<IScan>(
{
type: { type: String, required: true },
scans: Number
scans: { type: Number, required: true }
},
{ _id: false }
);

View File

@ -1,6 +1,6 @@
import { Loadout } from "@/src/models/inventoryModels/loadoutModel";
import { Loadout, TLoadoutDatabaseDocument } from "@/src/models/inventoryModels/loadoutModel";
export const getLoadout = async (accountId: string) => {
export const getLoadout = async (accountId: string): Promise<TLoadoutDatabaseDocument> => {
const loadout = await Loadout.findOne({ loadoutOwnerId: accountId });
if (!loadout) {

View File

@ -313,6 +313,12 @@ export const addMissionInventoryUpdates = async (
return inventoryChanges;
};
interface AddMissionRewardsReturnType {
MissionRewards: IMissionReward[];
inventoryChanges?: IInventoryChanges;
credits?: IMissionCredits;
}
//TODO: return type of partial missioninventoryupdate response
export const addMissionRewards = async (
inventory: TInventoryDatabaseDocument,
@ -324,7 +330,7 @@ export const addMissionRewards = async (
VoidTearParticipantsCurrWave: voidTearWave,
StrippedItems: strippedItems
}: IMissionInventoryUpdateRequest
) => {
): Promise<AddMissionRewardsReturnType> => {
if (!rewardInfo) {
//TODO: if there is a case where you can have credits collected during a mission but no rewardInfo, add credits needs to be handled earlier
logger.debug(`Mission ${missions!.Tag} did not have Reward Info `);
@ -435,6 +441,13 @@ export const addMissionRewards = async (
return { inventoryChanges, MissionRewards, credits };
};
interface IMissionCredits {
MissionCredits: number[];
CreditBonus: number[];
TotalCredits: number[];
DailyMissionBonus?: boolean;
}
//creditBonus is not entirely accurate.
//TODO: consider ActiveBoosters
export const addCredits = (
@ -444,11 +457,11 @@ export const addCredits = (
missionCompletionCredits,
rngRewardCredits
}: { missionDropCredits: number; missionCompletionCredits: number; rngRewardCredits: number }
) => {
): IMissionCredits => {
const hasDailyCreditBonus = true;
const totalCredits = missionDropCredits + missionCompletionCredits + rngRewardCredits;
const finalCredits = {
const finalCredits: IMissionCredits = {
MissionCredits: [missionDropCredits, missionDropCredits],
CreditBonus: [missionCompletionCredits, missionCompletionCredits],
TotalCredits: [totalCredits, totalCredits]
@ -471,7 +484,7 @@ export const addFixedLevelRewards = (
rewards: IMissionRewardExternal,
inventory: TInventoryDatabaseDocument,
MissionRewards: IMissionReward[]
) => {
): number => {
let missionBonusCredits = 0;
if (rewards.credits) {
missionBonusCredits += rewards.credits;

View File

@ -1,7 +1,8 @@
import { PersonalRooms } from "@/src/models/personalRoomsModel";
import { addItem, getInventory } from "@/src/services/inventoryService";
import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes";
export const getPersonalRooms = async (accountId: string) => {
export const getPersonalRooms = async (accountId: string): Promise<TPersonalRoomsDatabaseDocument> => {
const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId });
if (!personalRooms) {
@ -10,7 +11,7 @@ export const getPersonalRooms = async (accountId: string) => {
return personalRooms;
};
export const updateShipFeature = async (accountId: string, shipFeature: string) => {
export const updateShipFeature = async (accountId: string, shipFeature: string): Promise<void> => {
const personalRooms = await getPersonalRooms(accountId);
if (personalRooms.Ship.Features.includes(shipFeature)) {

View File

@ -108,8 +108,11 @@ export const handlePurchase = async (
];
}
purchaseRequest.PurchaseParams.Quantity *= offer.QuantityMultiplier;
} else if (!ExportVendors[purchaseRequest.PurchaseParams.SourceId!]) {
throw new Error(`unknown vendor: ${purchaseRequest.PurchaseParams.SourceId!}`);
} else {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!ExportVendors[purchaseRequest.PurchaseParams.SourceId!]) {
throw new Error(`unknown vendor: ${purchaseRequest.PurchaseParams.SourceId!}`);
}
}
}
@ -120,8 +123,6 @@ export const handlePurchase = async (
);
combineInventoryChanges(purchaseResponse.InventoryChanges, inventoryChanges);
if (!purchaseResponse) throw new Error("purchase response was undefined");
const currencyChanges = updateCurrency(
inventory,
purchaseRequest.PurchaseParams.ExpectedPrice,
@ -149,6 +150,7 @@ export const handlePurchase = async (
];
} else {
const syndicate = ExportSyndicates[syndicateTag];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (syndicate) {
const favour = syndicate.favours.find(
x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem
@ -360,6 +362,7 @@ const handleBoosterPackPurchase = async (
quantity: number
): Promise<IPurchaseResponse> => {
const pack = ExportBoosterPacks[typeName];
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!pack) {
throw new Error(`unknown booster pack: ${typeName}`);
}

View File

@ -93,7 +93,10 @@ export const updateQuestStage = (
Object.assign(questStage, questStageUpdate);
};
export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQuestKeyDatabase) => {
export const addQuestKey = (
inventory: TInventoryDatabaseDocument,
questKey: IQuestKeyDatabase
): IQuestKeyClient | undefined => {
if (inventory.QuestKeys.some(q => q.ItemType === questKey.ItemType)) {
logger.warn(`Quest key ${questKey.ItemType} already exists. It will not be added`);
return;
@ -115,7 +118,7 @@ export const addQuestKey = (inventory: TInventoryDatabaseDocument, questKey: IQu
return inventory.QuestKeys[index - 1].toJSON<IQuestKeyClient>();
};
export const completeQuest = async (inventory: TInventoryDatabaseDocument, questKey: string) => {
export const completeQuest = async (inventory: TInventoryDatabaseDocument, questKey: string): Promise<void> => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
const chainStages = ExportKeys[questKey]?.chainStages;

View File

@ -1,11 +1,10 @@
import { Ship } from "@/src/models/shipModel";
import { ILoadoutDatabase } from "@/src/types/saveLoadoutTypes";
import { Ship, TShipDatabaseDocument } from "@/src/models/shipModel";
import { Types } from "mongoose";
export const createShip = async (
accountOwnerId: Types.ObjectId,
typeName: string = "/Lotus/Types/Items/Ships/DefaultShip"
) => {
): Promise<Types.ObjectId> => {
try {
const ship = new Ship({
ItemType: typeName,
@ -21,7 +20,7 @@ export const createShip = async (
}
};
export const getShip = async (shipId: Types.ObjectId, fieldSelection: string = "") => {
export const getShip = async (shipId: Types.ObjectId, fieldSelection: string = ""): Promise<TShipDatabaseDocument> => {
const ship = await Ship.findOne({ _id: shipId }, fieldSelection);
if (!ship) {
@ -30,15 +29,3 @@ export const getShip = async (shipId: Types.ObjectId, fieldSelection: string = "
return ship;
};
export const getShipLean = async (shipOwnerId: string) => {
OrdisPrime marked this conversation as resolved
Review

only because this function is currently not used, does not mean it has to be deleted.

only because this function is currently not used, does not mean it has to be deleted.
Review

I don't even understand the intended purpose of it. Under which circumstances would you ever want an unhydrated ship combined with the loadout presets?

I don't even understand the intended purpose of it. Under which circumstances would you ever want an unhydrated ship combined with the loadout presets?
Review

hydration is ONLY needed when you want to use a mongoose document's functions.
So, all get requests should generally only use .lean() versions, so mongoose doesn't hydrate them (which takes lots of resources)

hydration is ONLY needed when you want to use a mongoose document's functions. So, all get requests should generally only use .lean() versions, so mongoose doesn't hydrate them (which takes lots of resources)
Review

I left the function there as a reminder to do that

I left the function there as a reminder to do that
Review

lean also seems to skip casing and validation, potentially invalidating the types.

`lean` also seems to skip casing and validation, potentially invalidating the types.
Review

I am not aware of that.
https://github.com/Automattic/mongoose/blob/master/docs/tutorials/lean.md
it describes getters as a good use case for using lean, which makes sense to me

I am not aware of that. https://github.com/Automattic/mongoose/blob/master/docs/tutorials/lean.md it describes getters as a good use case for using lean, which makes sense to me
Review

Literally search for "Casting and validation" on that page.

Literally search for "Casting and validation" on that page.
Review

The ship model also makes use of transformations, so we'd not get a correct client representation if we used lean.

The ship model also makes use of transformations, so we'd not get a correct client representation if we used `lean`.
Review

Right, however I don't think validation or casting is required during retrieval of a document, since that also happens during saving, so it should be impossible to even have invalid objects. Other than when someone would manually change stuff, but accounting for that possibility does not seem reasonable.

Since we use virtuals, and they rely on hydration apparently, we cannot use .lean() indeed.

However, your change was sus to me, because you just removed it while it had a purpose. What we discovered now does not change that removing stuff just like that is not so great

Right, however I don't think validation or casting is required during retrieval of a document, since that also happens during saving, so it should be impossible to even have invalid objects. Other than when someone would manually change stuff, but accounting for that possibility does not seem reasonable. Since we use virtuals, and they rely on hydration apparently, we cannot use .lean() indeed. However, your change was sus to me, because you just removed it while it had a purpose. What we discovered now does not change that removing stuff just like that is not so great
Review

I removed it because it didn't serve any apparent purpose and did not have a return type, hence raising a warning.

I removed it because it didn't serve any apparent purpose and did not have a return type, hence raising a warning.
const ship = await Ship.findOne({ ShipOwnerId: shipOwnerId }).lean().populate<{
LoadOutInventory: { LoadOutPresets: ILoadoutDatabase };
}>("LoadOutInventory.LoadOutPresets");
if (!ship) {
throw new Error(`error finding a ship for account ${shipOwnerId}`);
}
return ship;
};

View File

@ -82,7 +82,6 @@ export const updateStats = async (playerStats: TStatsDatabaseDocument, payload:
for (const [type, scans] of Object.entries(data as IUploadEntry)) {
const scan = playerStats.Scans.find(element => element.type === type);
if (scan) {
scan.scans ??= 0;
scan.scans += scans;
} else {
playerStats.Scans.push({ type: type, scans });
@ -95,7 +94,6 @@ export const updateStats = async (playerStats: TStatsDatabaseDocument, payload:
for (const [type, used] of Object.entries(data as IUploadEntry)) {
const ability = playerStats.Abilities.find(element => element.type === type);
if (ability) {
ability.used ??= 0;
ability.used += used;
} else {
playerStats.Abilities.push({ type: type, used });
@ -307,22 +305,20 @@ export const updateStats = async (playerStats: TStatsDatabaseDocument, payload:
for (const [category, value] of Object.entries(actionData as IStatsSet)) {
switch (category) {
case "ELO_RATING":
playerStats.Rating = value;
playerStats.Rating = value as number;
break;
case "RANK":
playerStats.Rank = value;
playerStats.Rank = value as number;
break;
case "PLAYER_LEVEL":
playerStats.PlayerLevel = value;
playerStats.PlayerLevel = value as number;
break;
default:
if (!ignoredCategories.includes(category)) {
if (!unknownCategories[action]) {
unknownCategories[action] = [];
}
unknownCategories[action] ??= [];
unknownCategories[action].push(category);
}
break;

View File

@ -7,7 +7,7 @@ import {
ITailorShopDatabase,
TBootLocation
} from "@/src/types/shipTypes";
import { Model, Types } from "mongoose";
import { Document, Model, Types } from "mongoose";
export interface IOrbiter {
Features: string[];
@ -48,3 +48,15 @@ export type PersonalRoomsDocumentProps = {
// eslint-disable-next-line @typescript-eslint/ban-types
export type PersonalRoomsModelType = Model<IPersonalRoomsDatabase, {}, PersonalRoomsDocumentProps>;
// eslint-disable-next-line @typescript-eslint/ban-types
export type TPersonalRoomsDatabaseDocument = Document<unknown, {}, IPersonalRoomsDatabase> &
Omit<
IPersonalRoomsDatabase & {
_id: Types.ObjectId;
} & {
__v: number;
},
keyof PersonalRoomsDocumentProps
> &
PersonalRoomsDocumentProps;

View File

@ -1,29 +1,29 @@
export interface ISession {
sessionId: string;
creatorId: string;
maxPlayers: number;
minPlayers: number;
privateSlots: number;
scoreLimit: number;
timeLimit: number;
gameModeId: number;
eloRating: number;
regionId: number;
difficulty: number;
hasStarted: boolean;
enableVoice: boolean;
matchType: string;
maps: string[];
originalSessionId: string;
customSettings: string;
rewardSeed: number;
guildId: string;
buildId: number;
platform: number;
xplatform: boolean;
freePublic: number;
freePrivate: number;
fullReset: number;
maxPlayers?: number;
minPlayers?: number;
privateSlots?: number;
scoreLimit?: number;
timeLimit?: number;
gameModeId?: number;
eloRating?: number;
regionId?: number;
difficulty?: number;
hasStarted?: boolean;
enableVoice?: boolean;
matchType?: string;
maps?: string[];
originalSessionId?: string;
customSettings?: string;
rewardSeed?: number;
guildId?: string;
buildId?: number;
platform?: number;
xplatform?: boolean;
freePublic?: number;
freePrivate?: number;
fullReset?: number;
}
export interface IFindSessionRequest {