Enable Biome rules 2

This commit is contained in:
Kalle
2026-09-06 14:00:27 +03:00
parent 8079fceb22
commit 3cf79bb6c1
41 changed files with 183 additions and 148 deletions

View File

@@ -19,7 +19,7 @@ interface QueuedToast {
class ToastQueue {
private toasts: QueuedToast[] = [];
private listeners = new Set<() => void>();
private readonly listeners = new Set<() => void>();
private counter = 0;
add(content: SendouToast, options?: { timeout?: number }) {

View File

@@ -201,7 +201,7 @@ function specialPoint({
const { effect } = abilityPointsToEffects({
abilityPoints: apFromMap({
abilityPoints: abilityPoints,
abilityPoints,
ability: SPECIAL_POINT_ABILITY,
}),
key: "IncreaseRt_Special",
@@ -252,7 +252,7 @@ function specialLost(
const { baseEffect, effect } = abilityPointsToEffects({
abilityPoints: apFromMap({
abilityPoints: abilityPoints,
abilityPoints,
ability: SPECIAL_SAVED_AFTER_DEATH_ABILITY,
}),
key: "SpecialGaugeRt_Restart",

View File

@@ -13,7 +13,10 @@ import type { ServerEvent } from "./events-types";
class FakeEventSource {
static instances: FakeEventSource[] = [];
private listeners = new Map<string, Set<(event: MessageEvent) => void>>();
private readonly listeners = new Map<
string,
Set<(event: MessageEvent) => void>
>();
url: string;

View File

@@ -15,7 +15,7 @@ export type DbMapPoolList = Array<
>;
export class MapPool {
private source: string | ReadonlyMapPoolObject;
private readonly source: string | ReadonlyMapPoolObject;
private asSerialized?: string;
private asObject?: ReadonlyMapPoolObject;

View File

@@ -287,11 +287,11 @@ export default function Planner() {
type: "image",
x: point[0],
y: point[1],
isLocked: isLocked,
isLocked,
id: shapeId,
meta: meta ?? {},
props: {
assetId: assetId,
assetId,
w: size[0],
h: size[1],
},

View File

@@ -408,7 +408,7 @@ function DamageRateInfoSection({
damageMultipliers[String(weaponId)]?.find((m) => m.target === target);
return (
<Fragment>
<>
<tr>
<td
colSpan={visibleWeaponIds.length + 1}
@@ -467,7 +467,7 @@ function DamageRateInfoSection({
</tr>
);
})}
</Fragment>
</>
);
}

View File

@@ -102,8 +102,8 @@ interface DetectorState {
}
export class DetectorScheduler {
#options: SchedulerOptions;
#states = new Map<string, DetectorState>();
readonly #options: SchedulerOptions;
readonly #states = new Map<string, DetectorState>();
#maxT = Number.NEGATIVE_INFINITY;
#lastActivityT = Number.NEGATIVE_INFINITY;
#matchOpenUntilT = Number.NEGATIVE_INFINITY;

View File

@@ -77,8 +77,8 @@ export type TimelineAction =
| { action: "dropped"; reason: "low-confidence" };
export class TimelineBuilder {
#events: DetectedEvent[] = [];
#options: TimelineOptions;
readonly #events: DetectedEvent[] = [];
readonly #options: TimelineOptions;
constructor(options: Partial<TimelineOptions> = {}) {
this.#options = { ...DEFAULT_TIMELINE_OPTIONS, ...options };

View File

@@ -81,10 +81,12 @@ export function sameScoreboardMatch(a: unknown, b: unknown): boolean {
// are distinctive per match — near-zero overlap means a different board
const pa = paints(da);
const pb = paints(db);
if (pa.length >= PAINT_MIN_READ && pb.length >= PAINT_MIN_READ) {
if (multisetOverlap(pa, pb) < Math.min(pa.length, pb.length) / 2)
return false;
}
if (
pa.length >= PAINT_MIN_READ &&
pb.length >= PAINT_MIN_READ &&
multisetOverlap(pa, pb) < Math.min(pa.length, pb.length) / 2
)
return false;
return true;
}

View File

@@ -43,13 +43,13 @@ interface QueuedFrame {
}
export class AnalyzerClient {
#worker: Worker;
readonly #worker: Worker;
#ready = false;
#busy = false;
#onResult: ResultHandler;
#onError: ErrorHandler;
#onDone: DoneHandler | undefined;
#readyPromise: Promise<void>;
readonly #onResult: ResultHandler;
readonly #onError: ErrorHandler;
readonly #onDone: DoneHandler | undefined;
readonly #readyPromise: Promise<void>;
#rejectReady: ((error: Error) => void) | undefined;
#idleWaiters: (() => void)[] = [];
#chunk: PendingChunk | null = null;

View File

@@ -41,7 +41,7 @@ export async function calculateMatchSkills({
for (const [index, userId] of winner.entries()) {
newSkills.push({
groupMatchId: groupMatchId,
groupMatchId,
identifier: null,
mu: winnerTeamNew[index].mu,
season,
@@ -52,7 +52,7 @@ export async function calculateMatchSkills({
for (const [index, userId] of loser.entries()) {
newSkills.push({
groupMatchId: groupMatchId,
groupMatchId,
identifier: null,
mu: loserTeamNew[index].mu,
season,
@@ -75,7 +75,7 @@ export async function calculateMatchSkills({
);
newSkills.push({
groupMatchId: groupMatchId,
groupMatchId,
identifier: winnerTeamIdentifier,
mu: winnerGroupNew.mu,
season,
@@ -83,7 +83,7 @@ export async function calculateMatchSkills({
userId: null,
});
newSkills.push({
groupMatchId: groupMatchId,
groupMatchId,
identifier: loserTeamIdentifier,
mu: loserGroupNew.mu,
season,

View File

@@ -22,7 +22,7 @@ export function cachedStreams() {
return cachified({
key: SENDOUQ_STREAMS_KEY,
cache: cache,
cache,
ttl: ttl(IN_MILLISECONDS.HALF_HOUR),
async getFreshValue() {
return streamedMatches({

View File

@@ -57,10 +57,10 @@ const SECONDS_TILL_STALE =
class SendouQClass {
groups;
#recentMatches;
#isAccurateTiers;
#userSkills;
#intervals;
readonly #recentMatches;
readonly #isAccurateTiers;
readonly #userSkills;
readonly #intervals;
usersInQueue;
constructor(

View File

@@ -30,7 +30,7 @@ export const action = async ({ params }: ActionFunctionArgs) => {
);
logger.info("Unlinking player", {
id: id,
id,
userId: user.id,
});

View File

@@ -274,7 +274,7 @@ export class SwissBracket extends Bracket {
teamId: winner.id,
setWins: 1,
setLosses: 0,
mapWins: mapWins,
mapWins,
mapLosses: 0,
});
}

View File

@@ -114,13 +114,13 @@ export class Tournament {
ctx;
/** See {@link TournamentArgs.participatedUsers}, null when this view did not get them. */
readonly participatedUserIds: number[] | null;
private args;
private data;
private _brackets: Array<Bracket | undefined> = [];
private readonly args;
private readonly data;
private readonly _brackets: Array<Bracket | undefined> = [];
private _allBrackets: Bracket[] | undefined;
private _derivedMeta: BracketDerivedMeta[] | undefined;
private _bracketsMeta: BracketMeta[] | undefined;
private bracketIdxsBeingBuilt = new Set<number>();
private readonly bracketIdxsBeingBuilt = new Set<number>();
constructor(args: TournamentArgs) {
const { data, ctx, bracketsMeta, brackets } = args;

View File

@@ -63,7 +63,7 @@ export class StageCreator {
slots: ParticipantSlot[],
): void {
const groupId = this.insertGroup({
stageId: stageId,
stageId,
number,
});
@@ -87,7 +87,7 @@ export class StageCreator {
slotsB: ParticipantSlot[],
): void {
const groupId = this.insertGroup({
stageId: stageId,
stageId,
number,
});
@@ -105,7 +105,7 @@ export class StageCreator {
): StandardBracketResults {
const roundCount = helpers.getUpperBracketRoundCount(slots.length);
const groupId = this.insertGroup({
stageId: stageId,
stageId,
number,
});
@@ -139,7 +139,7 @@ export class StageCreator {
const ordered = ordering[method](losers[losersId++]);
const groupId = this.insertGroup({
stageId: stageId,
stageId,
number,
});
@@ -179,7 +179,7 @@ export class StageCreator {
duels: Duel[],
): void {
const groupId = this.insertGroup({
stageId: stageId,
stageId,
number,
});
@@ -196,8 +196,8 @@ export class StageCreator {
): void {
const roundId = this.insertRound({
number: roundNumber,
stageId: stageId,
groupId: groupId,
stageId,
groupId,
});
for (let i = 0; i < matchCount; i++) {
@@ -225,9 +225,9 @@ export class StageCreator {
this.insertMatch({
number: matchNumber,
stageId: stageId,
groupId: groupId,
roundId: roundId,
stageId,
groupId,
roundId,
opponent1,
opponent2,
winnerSide: null,
@@ -235,11 +235,10 @@ export class StageCreator {
}
/** No ordering for major rounds (the first round must be ordered beforehand), LB minor rounds use the given method. */
getCurrentDuels(previousDuels: Duel[], currentDuelCount: number): Duel[];
getCurrentDuels(
previousDuels: Duel[],
currentDuelCount: number,
major: true,
major?: true,
): Duel[];
getCurrentDuels(
previousDuels: Duel[],

View File

@@ -339,9 +339,9 @@ function assertRoundRobin(input: number[], output: [number, number][][]): void {
const matchPerRound = Math.floor(n / 2);
const roundCount = n % 2 === 0 ? n - 1 : n;
if (output.length !== roundCount) throw Error("Round count is wrong");
if (output.length !== roundCount) throw new Error("Round count is wrong");
if (!output.every((round) => round.length === matchPerRound))
throw Error("Not every round has the good number of matches");
throw new Error("Not every round has the good number of matches");
const checkAllOpponents = Object.fromEntries(
input.map((element) => [element, new Set<number>()]),
@@ -351,22 +351,22 @@ function assertRoundRobin(input: number[], output: [number, number][][]): void {
const checkUnique = new Set<number>();
for (const match of round) {
if (match.length !== 2) throw Error("One match is not a pair");
if (match.length !== 2) throw new Error("One match is not a pair");
if (checkUnique.has(match[0]))
throw Error("This team is already playing");
throw new Error("This team is already playing");
checkUnique.add(match[0]);
if (checkUnique.has(match[1]))
throw Error("This team is already playing");
throw new Error("This team is already playing");
checkUnique.add(match[1]);
if (checkAllOpponents[match[0]].has(match[1]))
throw Error("The team has already matched this team");
throw new Error("The team has already matched this team");
checkAllOpponents[match[0]].add(match[1]);
if (checkAllOpponents[match[1]].has(match[0]))
throw Error("The team has already matched this team");
throw new Error("The team has already matched this team");
checkAllOpponents[match[1]].add(match[0]);
}
}
@@ -380,9 +380,9 @@ function assertAbDivisionRoundRobin(
const roundCount = Math.max(divisionA.length, divisionB.length);
const matchesPerRound = Math.min(divisionA.length, divisionB.length);
if (output.length !== roundCount) throw Error("Round count is wrong");
if (output.length !== roundCount) throw new Error("Round count is wrong");
if (!output.every((round) => round.length === matchesPerRound))
throw Error("Not every round has the good number of matches");
throw new Error("Not every round has the good number of matches");
const aSet = new Set(divisionA);
const bSet = new Set(divisionB);
@@ -392,26 +392,28 @@ function assertAbDivisionRoundRobin(
const playingInRound = new Set<number>();
for (const match of round) {
if (match.length !== 2) throw Error("One match is not a pair");
if (match.length !== 2) throw new Error("One match is not a pair");
const [a, b] = match;
if (!aSet.has(a)) throw Error(`${a} is not a division A participant`);
if (!bSet.has(b)) throw Error(`${b} is not a division B participant`);
if (!aSet.has(a)) throw new Error(`${a} is not a division A participant`);
if (!bSet.has(b)) throw new Error(`${b} is not a division B participant`);
if (playingInRound.has(a)) throw Error("This team is already playing");
if (playingInRound.has(a))
throw new Error("This team is already playing");
playingInRound.add(a);
if (playingInRound.has(b)) throw Error("This team is already playing");
if (playingInRound.has(b))
throw new Error("This team is already playing");
playingInRound.add(b);
const pairingKey = `${a}-${b}`;
if (seenPairings.has(pairingKey))
throw Error("The teams have already been paired");
throw new Error("The teams have already been paired");
seenPairings.add(pairingKey);
}
}
if (seenPairings.size !== divisionA.length * divisionB.length)
throw Error("Not every A vs B pairing was generated");
throw new Error("Not every A vs B pairing was generated");
}

View File

@@ -80,11 +80,12 @@ export function makeAbDivisionGroups<T>(
divisionB: T[],
groupCount: number,
): { a: T[]; b: T[] }[] {
if (groupCount <= 0) throw Error("Group count must be strictly positive.");
if (groupCount <= 0)
throw new Error("Group count must be strictly positive.");
if (divisionA.length !== divisionB.length) {
if (groupCount !== 1)
throw Error(
throw new Error(
"Uneven A/B divisions are only supported with a single group.",
);
@@ -92,7 +93,7 @@ export function makeAbDivisionGroups<T>(
}
if (divisionA.length % groupCount !== 0)
throw Error("Pool size must be divisible by group count.");
throw new Error("Pool size must be divisible by group count.");
const aOrdered = ordering["groups.seed_optimized"](divisionA, groupCount);
const bOrdered = ordering["groups.seed_optimized"](divisionB, groupCount);
@@ -152,14 +153,16 @@ export function ensureValidSize(
participantCount: number,
): void {
if (participantCount < 2)
throw Error("Impossible to create a stage with less than 2 participants.");
throw new Error(
"Impossible to create a stage with less than 2 participants.",
);
if (stageType === "round_robin") {
return;
}
if (!Number.isInteger(Math.log2(participantCount)))
throw Error(
throw new Error(
"The library only supports a participant count which is a power of two.",
);
}

View File

@@ -50,7 +50,7 @@ export function createResolved(input: ResolvedCreateBracketInput): BracketData {
createDoubleElimination(creator);
break;
default:
throw Error("Unknown stage type.");
throw new Error("Unknown stage type.");
}
return creator.data;
@@ -65,7 +65,8 @@ function attachRoundMaps(
const resolveRound = (roundId: number) => {
const round = roundsById.get(roundId);
if (!round) throw Error(`No round found for map list round id ${roundId}`);
if (!round)
throw new Error(`No round found for map list round id ${roundId}`);
return round;
};
@@ -75,7 +76,7 @@ function attachRoundMaps(
data.round.map((round) => round.number),
).size;
if (mapsInput.length !== distinctRoundNumberCount) {
throw Error("Invalid map list count");
throw new Error("Invalid map list count");
}
const mapsByRoundNumber = new Map(
@@ -87,7 +88,8 @@ function attachRoundMaps(
for (const round of data.round) {
const maps = mapsByRoundNumber.get(round.number);
if (!maps) throw Error(`No maps found for round number ${round.number}`);
if (!maps)
throw new Error(`No maps found for round number ${round.number}`);
round.maps = { ...maps };
}
@@ -95,7 +97,7 @@ function attachRoundMaps(
}
if (mapsInput.length !== data.round.length) {
throw Error("Invalid map list count");
throw new Error("Invalid map list count");
}
for (const input of mapsInput) {
@@ -103,7 +105,7 @@ function attachRoundMaps(
}
for (const round of data.round) {
if (!round.maps) throw Error(`Round id ${round.id} is missing maps`);
if (!round.maps) throw new Error(`Round id ${round.id} is missing maps`);
}
}

View File

@@ -37,10 +37,10 @@ function getRoundRobinGroups(creator: StageCreator): ParticipantSlot[][] {
creator.settings.groupCount === undefined ||
!Number.isInteger(creator.settings.groupCount)
)
throw Error("You must specify a group count for round-robin stages.");
throw new Error("You must specify a group count for round-robin stages.");
if (creator.settings.groupCount <= 0)
throw Error("You must provide a strictly positive group count.");
throw new Error("You must provide a strictly positive group count.");
const slots = creator.getSlots();
const ordered = ordering["groups.seed_optimized"](
@@ -59,19 +59,21 @@ function getAbDivisionGroups(creator: StageCreator): {
creator.settings.groupCount === undefined ||
!Number.isInteger(creator.settings.groupCount)
)
throw Error("You must specify a group count for round-robin stages.");
throw new Error("You must specify a group count for round-robin stages.");
if (creator.settings.groupCount <= 0)
throw Error("You must provide a strictly positive group count.");
throw new Error("You must provide a strictly positive group count.");
const abDivisions = creator.input.abDivisions;
if (!abDivisions)
throw Error("abDivisions must be provided when hasAbDivisions is enabled.");
throw new Error(
"abDivisions must be provided when hasAbDivisions is enabled.",
);
const slots = creator.getSlots();
if (abDivisions.length !== slots.length)
throw Error("abDivisions length must match the seeding length.");
throw new Error("abDivisions length must match the seeding length.");
const divisionA: ParticipantSlot[] = [];
const divisionB: ParticipantSlot[] = [];
@@ -79,13 +81,13 @@ function getAbDivisionGroups(creator: StageCreator): {
for (let i = 0; i < slots.length; i++) {
const slot = slots[i];
if (slot === null)
throw Error("BYEs are not supported with A/B divisions.");
throw new Error("BYEs are not supported with A/B divisions.");
const division = abDivisions[i];
if (division === 0) divisionA.push(slot);
else if (division === 1) divisionB.push(slot);
else
throw Error(
throw new Error(
`Participant at seed ${i + 1} is missing an A/B division assignment.`,
);
}

View File

@@ -86,7 +86,7 @@ function firstRoundMatches({
id: matchId++,
groupId: groupIdx,
stageId: 0,
roundId: roundId,
roundId,
number: i + 1,
opponent1: {
id: upper,
@@ -103,7 +103,7 @@ function firstRoundMatches({
id: matchId++,
groupId: groupIdx,
stageId: 0,
roundId: roundId,
roundId,
number: upperHalf.length + 1,
opponent1: {
id: bye,

View File

@@ -32,7 +32,7 @@ export function endDroppedTeamMatches(
})();
const stored = store.matchById(match.id);
if (!stored) throw Error("Match not found.");
if (!stored) throw new Error("Match not found.");
propagator.updateMatch(
stored,

View File

@@ -17,13 +17,13 @@ export function resetMatchResults(
const propagator = new Propagator(store);
const stored = store.matchById(matchId);
if (!stored) throw Error("Match not found.");
if (!stored) throw new Error("Match not found.");
const stage = store.stageById(stored.stageId);
if (!stage) throw Error("Stage not found.");
if (!stage) throw new Error("Stage not found.");
const group = store.groupById(stored.groupId);
if (!group) throw Error("Group not found.");
if (!group) throw new Error("Group not found.");
const { roundNumber, roundCount } = propagator.getRoundPositionalInfo(
stored.roundId,
@@ -48,7 +48,7 @@ export function resetMatchResults(
!isMatchByeCompleted(match),
)
)
throw Error("The match is locked.");
throw new Error("The match is locked.");
helpers.clearWinner(stored);
store.markMatchChanged(stored);

View File

@@ -123,5 +123,5 @@ function sideOfTeam(match: MatchData, teamId: number): 0 | 1 {
if (match.opponent1?.id === teamId) return 0;
if (match.opponent2?.id === teamId) return 1;
throw Error(`Team id ${teamId} is not in the match`);
throw new Error(`Team id ${teamId} is not in the match`);
}

View File

@@ -82,7 +82,7 @@ export class Store {
/** Records that a match row of this store was mutated. */
markMatchChanged(match: MatchData): void {
if (this.matchesById.get(match.id) !== match)
throw Error("Match is not a row of this store.");
throw new Error("Match is not a row of this store.");
this.changedMatchIds.add(match.id);
}

View File

@@ -31,10 +31,10 @@ export class Propagator {
);
const stage = this.store.stageById(match.stageId);
if (!stage) throw Error("Stage not found.");
if (!stage) throw new Error("Stage not found.");
const group = this.store.groupById(match.groupId);
if (!group) throw Error("Group not found.");
if (!group) throw new Error("Group not found.");
const matchLocation = helpers.getMatchLocation(stage.type, group.number);
@@ -48,10 +48,10 @@ export class Propagator {
force?: boolean,
): void {
if (!force && matchStatus(this.store.data, stored.id) === "PENDING")
throw Error("The match is locked.");
throw new Error("The match is locked.");
const stage = this.store.stageById(stored.stageId);
if (!stage) throw Error("Stage not found.");
if (!stage) throw new Error("Stage not found.");
const resultChanged = helpers.setMatchResults(
stored,
@@ -137,7 +137,7 @@ export class Propagator {
winnerSide?: Side,
): void {
if (matchLocation === "final_group") {
if (!nextMatches[0]) throw Error("First next match is null.");
if (!nextMatches[0]) throw new Error("First next match is null.");
setNextOpponent(nextMatches[0], "opponent1", match, "opponent1");
setNextOpponent(nextMatches[0], "opponent2", match, "opponent2");
this.store.markMatchChanged(nextMatches[0]);
@@ -157,7 +157,7 @@ export class Propagator {
}
if (nextMatches.length !== 2) return;
if (!nextMatches[1]) throw Error("Second next match is null.");
if (!nextMatches[1]) throw new Error("Second next match is null.");
// Second match is the consolation final (SE) or a loser bracket match (DE).
if (matchLocation === "single_bracket") {
@@ -193,7 +193,7 @@ export class Propagator {
getRoundPositionalInfo(roundId: number): RoundPositionalInfo {
const round = this.store.roundById(roundId);
if (!round) throw Error("Round not found.");
if (!round) throw new Error("Round not found.");
return {
roundNumber: round.number,
@@ -228,7 +228,7 @@ export class Propagator {
case "final_group":
return this.getNextMatchesFinal(match, roundNumber, roundCount);
default:
throw Error("Unknown bracket kind.");
throw new Error("Unknown bracket kind.");
}
}
@@ -378,7 +378,7 @@ export class Propagator {
/** The only bracket in single elimination, the winner bracket in double elimination. */
private getUpperBracket(stageId: number): GroupData {
const winnerBracket = this.store.groupByNumber(stageId, 1);
if (!winnerBracket) throw Error("Winner bracket not found.");
if (!winnerBracket) throw new Error("Winner bracket not found.");
return winnerBracket;
}
@@ -386,7 +386,7 @@ export class Propagator {
private participantCount(stageId: number): number {
const upperBracket = this.getUpperBracket(stageId);
const firstRound = this.store.roundByNumber(upperBracket.id, 1);
if (!firstRound) throw Error("First round not found.");
if (!firstRound) throw new Error("First round not found.");
return this.store.matchCountInRound(firstRound.id) * 2;
}
@@ -424,11 +424,11 @@ export class Propagator {
): MatchData {
const round = this.store.roundByNumber(groupId, roundNumber);
if (!round) throw Error("Round not found.");
if (!round) throw new Error("Round not found.");
const match = this.store.matchByNumber(round.id, matchNumber);
if (!match) throw Error("Match not found.");
if (!match) throw new Error("Match not found.");
return match;
}

View File

@@ -142,9 +142,9 @@ function sortRounds(
doubleEliminationGroupRank(b.groupId);
if (rankDiff !== 0) return rankDiff;
}
if (type === "single_elimination") {
// finals and 3rd place match last
if (a.groupId !== b.groupId) return a.groupId - b.groupId;
// finals and 3rd place match last
if (type === "single_elimination" && a.groupId !== b.groupId) {
return a.groupId - b.groupId;
}
return a.number - b.number;

View File

@@ -207,7 +207,7 @@ function NewBadgeReceiversSelector({
tournament.minMembersPerTeam === newOwnerStanding.members.length;
newReceivers.push({
badgeId: badgeId,
badgeId,
tournamentTeamId: newOwnerTournamentTeamId,
userIds: defaultSelected
? newOwnerStanding.members.map((m) => m.userId)

View File

@@ -435,7 +435,7 @@ export function getBracketProgressionLabel(
while (j < prefix.length && j < name.length && prefix[j] === name[j]) {
j++;
}
prefix = prefix.substring(0, j);
prefix = prefix.slice(0, j);
if (prefix === "") break;
}

View File

@@ -127,7 +127,7 @@ export function Trophy({
try {
viewer.setState(JSON.parse(modelState));
} catch (_) {
} catch {
setError(true);
return;
}

View File

@@ -206,7 +206,7 @@ function pastedTierListUrlToSearchParams(value: string) {
if (!value.includes("/tier-list-maker")) return value;
try {
return new URL(value, "https://sendou.ink").search.substring(1);
return new URL(value, "https://sendou.ink").search.slice(1);
} catch {
return value;
}

View File

@@ -38,7 +38,7 @@ const videoMatchSchema = v.object({
"Invalid time format. Use HH:MM:SS or MM:SS",
),
),
stageId: stageId,
stageId,
mode: modeShort,
weapons: v.array(weaponSplId),
});

View File

@@ -58,8 +58,7 @@ function getTickStore(everyMs: number, alignToMs: number): TickStore {
* Rerenders periodically, returning the latest tick's `Date`: consume it so React Compiler can't memoize the
* rerender away. Components on the same interval and `alignTo` share one timer, ticking at exactly `alignTo + N*interval`.
*/
export function useAutoRerender(): Date;
export function useAutoRerender(every: "second" | "ten seconds"): Date;
export function useAutoRerender(every?: "second" | "ten seconds"): Date;
export function useAutoRerender(
every: "minute",
options: AutoRerenderOptions,

View File

@@ -496,34 +496,34 @@ export const resources = {
welcome: welcomeEsUs,
},
en: {
gear: gear,
faq: faq,
forms: forms,
friends: friends,
weapons: weapons,
gear,
faq,
forms,
friends,
weapons,
schedule: scheduleEn,
scrims: scrimsEn,
settings: settings,
common: common,
settings,
common,
"game-badges": gameBadges,
"game-misc": gameMisc,
tournament: tournament,
front: front,
user: user,
q: q,
art: art,
builds: builds,
lfg: lfg,
vods: vods,
calendar: calendar,
org: org,
params: params,
badges: badges,
contributions: contributions,
team: team,
trophies: trophies,
tournament,
front,
user,
q,
art,
builds,
lfg,
vods,
calendar,
org,
params,
badges,
contributions,
team,
trophies,
"tier-list-maker": tierListMaker,
analyzer: analyzer,
analyzer,
welcome: welcomeEn,
},
ko: {

View File

@@ -25,11 +25,11 @@ export interface VirtualItem {
*/
export class VirtualizerCore {
private count: number;
private estimatedSize: number;
private gap: number;
private overscan: number;
private measuredSizes = new Map<number, number>();
private offsets: number[] = [];
private readonly estimatedSize: number;
private readonly gap: number;
private readonly overscan: number;
private readonly measuredSizes = new Map<number, number>();
private readonly offsets: number[] = [];
/** Entries of `offsets` before this index are up to date. */
private validOffsets = 0;

View File

@@ -2,7 +2,7 @@ import { logger } from "../utils/logger";
export class Routine {
readonly name;
private func;
private readonly func;
constructor({
name,

View File

@@ -15,7 +15,7 @@ export function compressToBase64(
const base64 = btoa(binary);
if (!options?.urlSafe) return base64;
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, "");
}
/**

View File

@@ -103,7 +103,7 @@ function compute_max_saturation(a: number, b: number): number {
const f1 = wl * l_dS + wm * m_dS + ws * s_dS;
const f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
S = S - (f * f1) / (f1 * f1 - 0.5 * f * f2);
S -= (f * f1) / (f1 * f1 - 0.5 * f * f2);
}
return S;

View File

@@ -99,6 +99,20 @@
"useSymbolDescription": "error",
"useGroupedAccessorPairs": "error",
"useSpreadOverApply": "error",
"useCollapsedIf": {
"fix": "safe",
"level": "error"
},
"useConsistentObjectDefinitions": {
"fix": "safe",
"level": "error"
},
"useThrowNewError": "error",
"useShorthandAssign": "error",
"useFragmentSyntax": "error",
"noSubstr": "error",
"useUnifiedTypeSignatures": "error",
"useReadonlyClassProperties": "error",
"useFilenamingConvention": {
"level": "error",
"options": {
@@ -122,7 +136,16 @@
"useWhile": {
"fix": "safe",
"level": "error"
}
},
"noUselessStringConcat": {
"fix": "safe",
"level": "error"
},
"noDivRegex": {
"fix": "safe",
"level": "error"
},
"noUselessCatchBinding": "error"
},
"performance": {
"noSyncScripts": "error"

View File

@@ -111,7 +111,7 @@ const NAME_GREEK = "ια"; // ι: "Rιppιng_H", α: "◇Dαrz™" (special-symb
* Scoreboard-names only (not death-tag) until attested, like NAME_GREEK.
*/
const NAME_SYMBOLS =
"′‘’‚‛…″“”„←→↑↓⇒⇔˜€∞√∀⊂⊃∴∵∂№♭♀♂◎◇◆△▲▽▼†※™" + "『』【】〈〉《》〔〕々〆〇〃~";
"′‘’‚‛…″“”„←→↑↓⇒⇔˜€∞√∀⊂⊃∴∵∂№♭♀♂◎◇◆△▲▽▼†※™『』【】〈〉《》〔〕々〆〇〃~";
/**
* Render the key char but emit it as the value: in-game names show "•" as a