mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-08 20:26:08 -05:00
Enable Biome rules 2
This commit is contained in:
@@ -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 }) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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],
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -30,7 +30,7 @@ export const action = async ({ params }: ActionFunctionArgs) => {
|
||||
);
|
||||
|
||||
logger.info("Unlinking player", {
|
||||
id: id,
|
||||
id,
|
||||
userId: user.id,
|
||||
});
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ export class SwissBracket extends Bracket {
|
||||
teamId: winner.id,
|
||||
setWins: 1,
|
||||
setLosses: 0,
|
||||
mapWins: mapWins,
|
||||
mapWins,
|
||||
mapLosses: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[],
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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`);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ export function Trophy({
|
||||
|
||||
try {
|
||||
viewer.setState(JSON.parse(modelState));
|
||||
} catch (_) {
|
||||
} catch {
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { logger } from "../utils/logger";
|
||||
|
||||
export class Routine {
|
||||
readonly name;
|
||||
private func;
|
||||
private readonly func;
|
||||
|
||||
constructor({
|
||||
name,
|
||||
|
||||
@@ -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(/[=]+$/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
25
biome.json
25
biome.json
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user