Fix circular dependency and client-bundle server-only code in simulator routes
NCAAM and NCAAW simulators imported getParticipantSimulatorInputs from models/simulator, which imports manifest.ts, which imports registry.ts — creating a cycle back through registry.ts → simulator files. This caused a TDZ ReferenceError in the client bundle. Fix: inline a direct seasonParticipantSimulatorInputs query in both simulators (they already run direct DB queries) instead of going through the model layer, breaking the cycle. Also moves getSimulatorInputPolicy from the component body to the loader on the simulator setup page. The component calling it pulled input-policy.ts → manifest.ts → registry.ts → all simulator implementations into the client bundle, which contain Node.js-only code and caused "Error loading route module" in production. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e5295812f6
commit
7c94657417
3 changed files with 18 additions and 9 deletions
|
|
@ -55,7 +55,9 @@ export async function loader({ params }: Route.LoaderArgs) {
|
|||
input: inputMap.get(participant.id) ?? null,
|
||||
}));
|
||||
|
||||
return { sportsSeason, participants, config, inputRows, readiness };
|
||||
const inputPolicy = getSimulatorInputPolicy(config.config);
|
||||
|
||||
return { sportsSeason, participants, config, inputRows, readiness, inputPolicy };
|
||||
}
|
||||
|
||||
interface ActionData {
|
||||
|
|
@ -234,12 +236,11 @@ export async function action({ request, params }: Route.ActionArgs): Promise<Act
|
|||
}
|
||||
|
||||
export default function AdminSportsSeasonSimulator({ loaderData }: Route.ComponentProps) {
|
||||
const { sportsSeason, config, inputRows, readiness } = loaderData;
|
||||
const { sportsSeason, config, inputRows, readiness, inputPolicy } = loaderData;
|
||||
const actionData = useActionData<ActionData>();
|
||||
const navigation = useNavigation();
|
||||
const isSubmitting = navigation.state === "submitting";
|
||||
const setupSections = config.profile.setupSections;
|
||||
const inputPolicy = getSimulatorInputPolicy(config.config);
|
||||
const sourceEloAlternatives = config.profile.derivableInputs?.sourceElo ?? [];
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ import type { BracketRegion } from "~/lib/bracket-templates";
|
|||
import { buildNCAA68SlotMap, matchIndexForSeedSlot, NCAA_68 } from "~/lib/bracket-templates";
|
||||
import type { Simulator, SimulationResult } from "./types";
|
||||
import { logger } from "~/lib/logger";
|
||||
import { getParticipantSimulatorInputs } from "~/models/simulator";
|
||||
|
||||
// ─── Simulation parameters ────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -499,7 +498,10 @@ export class NCAAMSimulator implements Simulator {
|
|||
.select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name })
|
||||
.from(schema.seasonParticipants)
|
||||
.where(inArray(schema.seasonParticipants.id, participantIds)),
|
||||
getParticipantSimulatorInputs(sportsSeasonId),
|
||||
db
|
||||
.select({ participantId: schema.seasonParticipantSimulatorInputs.participantId, rating: schema.seasonParticipantSimulatorInputs.rating })
|
||||
.from(schema.seasonParticipantSimulatorInputs)
|
||||
.where(eq(schema.seasonParticipantSimulatorInputs.sportsSeasonId, sportsSeasonId)),
|
||||
]);
|
||||
|
||||
if (participantRows.length < participantIds.length) {
|
||||
|
|
@ -514,7 +516,9 @@ export class NCAAMSimulator implements Simulator {
|
|||
const inputById = new Map(simulatorInputs.map((input) => [input.participantId, input]));
|
||||
const netRatingById = new Map<string, number>();
|
||||
for (const { id, name } of participantRows) {
|
||||
netRatingById.set(id, inputById.get(id)?.rating ?? getNetRating(name));
|
||||
const rawRating = inputById.get(id)?.rating;
|
||||
const rating = rawRating !== null && rawRating !== undefined ? Number(rawRating) : null;
|
||||
netRatingById.set(id, (rating !== null && Number.isFinite(rating) ? rating : null) ?? getNetRating(name));
|
||||
}
|
||||
|
||||
// 7. Build per-round O(1) lookup maps keyed by matchNumber.
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import type { BracketRegion } from "~/lib/bracket-templates";
|
|||
import { buildNCAA68SlotMap, matchIndexForSeedSlot, NCAA_68 } from "~/lib/bracket-templates";
|
||||
import type { Simulator, SimulationResult } from "./types";
|
||||
import { logger } from "~/lib/logger";
|
||||
import { getParticipantSimulatorInputs } from "~/models/simulator";
|
||||
|
||||
// ─── Simulation parameters ────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -462,7 +461,10 @@ export class NCAAWSimulator implements Simulator {
|
|||
.select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name })
|
||||
.from(schema.seasonParticipants)
|
||||
.where(inArray(schema.seasonParticipants.id, participantIds)),
|
||||
getParticipantSimulatorInputs(sportsSeasonId),
|
||||
db
|
||||
.select({ participantId: schema.seasonParticipantSimulatorInputs.participantId, rating: schema.seasonParticipantSimulatorInputs.rating })
|
||||
.from(schema.seasonParticipantSimulatorInputs)
|
||||
.where(eq(schema.seasonParticipantSimulatorInputs.sportsSeasonId, sportsSeasonId)),
|
||||
]);
|
||||
|
||||
if (participantRows.length < participantIds.length) {
|
||||
|
|
@ -477,7 +479,9 @@ export class NCAAWSimulator implements Simulator {
|
|||
const inputById = new Map(simulatorInputs.map((input) => [input.participantId, input]));
|
||||
const barthagById = new Map<string, number>();
|
||||
for (const { id, name } of participantRows) {
|
||||
barthagById.set(id, inputById.get(id)?.rating ?? getBarthagRating(name));
|
||||
const rawRating = inputById.get(id)?.rating;
|
||||
const rating = rawRating !== null && rawRating !== undefined ? Number(rawRating) : null;
|
||||
barthagById.set(id, (rating !== null && Number.isFinite(rating) ? rating : null) ?? getBarthagRating(name));
|
||||
}
|
||||
|
||||
// 7. Build per-round O(1) lookup maps keyed by matchNumber.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue