mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:50:43 +00:00
upgrade to zod 4 (#1161)
## Description: updates zod to v4 closes #873 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: omrih --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
+24
-18
@@ -1,3 +1,4 @@
|
||||
import { z } from "zod/v4";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import {
|
||||
AllPlayersStats,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
Intent,
|
||||
PlayerRecord,
|
||||
ServerMessage,
|
||||
ServerStartGameMessageSchema,
|
||||
ServerStartGameMessage,
|
||||
Turn,
|
||||
} from "../core/Schemas";
|
||||
import { createGameRecord, decompressGameRecord, replacer } from "../core/Util";
|
||||
@@ -75,14 +76,11 @@ export class LocalServer {
|
||||
if (this.lobbyConfig.gameStartInfo === undefined) {
|
||||
throw new Error("missing gameStartInfo");
|
||||
}
|
||||
this.clientMessage(
|
||||
ServerStartGameMessageSchema.parse({
|
||||
type: "start",
|
||||
gameID: this.lobbyConfig.gameStartInfo.gameID,
|
||||
gameStartInfo: this.lobbyConfig.gameStartInfo,
|
||||
turns: [],
|
||||
}),
|
||||
);
|
||||
this.clientMessage({
|
||||
type: "start",
|
||||
gameStartInfo: this.lobbyConfig.gameStartInfo,
|
||||
turns: [],
|
||||
} satisfies ServerStartGameMessage);
|
||||
}
|
||||
|
||||
pause() {
|
||||
@@ -94,9 +92,14 @@ export class LocalServer {
|
||||
}
|
||||
|
||||
onMessage(message: string) {
|
||||
const clientMsg: ClientMessage = ClientMessageSchema.parse(
|
||||
JSON.parse(message),
|
||||
);
|
||||
const result = ClientMessageSchema.safeParse(JSON.parse(message));
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.error("Error parsing client message", error);
|
||||
return;
|
||||
}
|
||||
|
||||
const clientMsg: ClientMessage = result.data;
|
||||
if (clientMsg.type === "intent") {
|
||||
if (this.lobbyConfig.gameRecord) {
|
||||
// If we are replaying a game, we don't want to process intents
|
||||
@@ -211,12 +214,15 @@ export class LocalServer {
|
||||
record.turns = [];
|
||||
}
|
||||
// For unload events, sendBeacon is the only reliable method
|
||||
const blob = new Blob(
|
||||
[JSON.stringify(GameRecordSchema.parse(record), replacer)],
|
||||
{
|
||||
type: "application/json",
|
||||
},
|
||||
);
|
||||
const result = GameRecordSchema.safeParse(record);
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.error("Error parsing game record", error);
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([JSON.stringify(result.data, replacer)], {
|
||||
type: "application/json",
|
||||
});
|
||||
const workerPath = this.lobbyConfig.serverConfig.workerPath(
|
||||
this.lobbyConfig.gameStartInfo.gameID,
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { z } from "zod/v4";
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import {
|
||||
AllPlayers,
|
||||
@@ -309,14 +310,13 @@ export class Transport {
|
||||
onconnect();
|
||||
};
|
||||
this.socket.onmessage = (event: MessageEvent) => {
|
||||
try {
|
||||
const serverMsg = ServerMessageSchema.parse(JSON.parse(event.data));
|
||||
this.onmessage(serverMsg);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to process server message ${event.data}: ${error}, ${error.stack}`,
|
||||
);
|
||||
const result = ServerMessageSchema.safeParse(JSON.parse(event.data));
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.error("Error parsing server message", error);
|
||||
return;
|
||||
}
|
||||
this.onmessage(result.data);
|
||||
};
|
||||
this.socket.onerror = (err) => {
|
||||
console.error("Socket encountered error: ", err, "Closing socket");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { base64urlToUuid } from "./Base64";
|
||||
|
||||
export const RefreshResponseSchema = z.object({
|
||||
|
||||
+15
-15
@@ -1,4 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import quickChatData from "../../resources/QuickChat.json" with { type: "json" };
|
||||
import {
|
||||
AllPlayers,
|
||||
@@ -76,14 +76,14 @@ export type ClientMessage =
|
||||
| ClientLogMessage
|
||||
| ClientHashMessage;
|
||||
export type ServerMessage =
|
||||
| ServerSyncMessage
|
||||
| ServerTurnMessage
|
||||
| ServerStartGameMessage
|
||||
| ServerPingMessage
|
||||
| ServerDesyncMessage
|
||||
| ServerPrestartMessage
|
||||
| ServerErrorMessage;
|
||||
|
||||
export type ServerSyncMessage = z.infer<typeof ServerTurnMessageSchema>;
|
||||
export type ServerTurnMessage = z.infer<typeof ServerTurnMessageSchema>;
|
||||
export type ServerStartGameMessage = z.infer<
|
||||
typeof ServerStartGameMessageSchema
|
||||
>;
|
||||
@@ -101,7 +101,7 @@ export type ClientHashMessage = z.infer<typeof ClientHashSchema>;
|
||||
export type AllPlayersStats = z.infer<typeof AllPlayersStatsSchema>;
|
||||
export type Player = z.infer<typeof PlayerSchema>;
|
||||
export type GameStartInfo = z.infer<typeof GameStartInfoSchema>;
|
||||
const PlayerTypeSchema = z.nativeEnum(PlayerType);
|
||||
const PlayerTypeSchema = z.enum(PlayerType);
|
||||
|
||||
export interface GameInfo {
|
||||
gameID: GameID;
|
||||
@@ -127,18 +127,18 @@ export enum LogSeverity {
|
||||
//
|
||||
|
||||
export const GameConfigSchema = z.object({
|
||||
gameMap: z.nativeEnum(GameMapType),
|
||||
difficulty: z.nativeEnum(Difficulty),
|
||||
gameType: z.nativeEnum(GameType),
|
||||
gameMode: z.nativeEnum(GameMode),
|
||||
gameMap: z.enum(GameMapType),
|
||||
difficulty: z.enum(Difficulty),
|
||||
gameType: z.enum(GameType),
|
||||
gameMode: z.enum(GameMode),
|
||||
disableNPCs: z.boolean(),
|
||||
bots: z.number().int().min(0).max(400),
|
||||
infiniteGold: z.boolean(),
|
||||
infiniteTroops: z.boolean(),
|
||||
instantBuild: z.boolean(),
|
||||
maxPlayers: z.number().optional(),
|
||||
disabledUnits: z.nativeEnum(UnitType).array().optional(),
|
||||
playerTeams: z.union([z.number().optional(), z.literal(Duos)]),
|
||||
disabledUnits: z.enum(UnitType).array().optional(),
|
||||
playerTeams: z.union([z.number(), z.literal(Duos)]).optional(),
|
||||
});
|
||||
|
||||
export const TeamSchema = z.string();
|
||||
@@ -150,8 +150,8 @@ const SafeString = z
|
||||
)
|
||||
.max(1000);
|
||||
|
||||
export const PersistentIdSchema = z.string().uuid();
|
||||
const JwtTokenSchema = z.string().jwt();
|
||||
export const PersistentIdSchema = z.uuid();
|
||||
const JwtTokenSchema = z.jwt();
|
||||
const TokenSchema = z
|
||||
.string()
|
||||
.refine(
|
||||
@@ -268,14 +268,14 @@ export const TargetTroopRatioIntentSchema = BaseIntentSchema.extend({
|
||||
|
||||
export const BuildUnitIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("build_unit"),
|
||||
unit: z.nativeEnum(UnitType),
|
||||
unit: z.enum(UnitType),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
});
|
||||
|
||||
export const UpgradeStructureIntentSchema = BaseIntentSchema.extend({
|
||||
type: z.literal("upgrade_structure"),
|
||||
unit: z.nativeEnum(UnitType),
|
||||
unit: z.enum(UnitType),
|
||||
unitId: z.number(),
|
||||
});
|
||||
|
||||
@@ -426,7 +426,7 @@ export const ClientHashSchema = z.object({
|
||||
|
||||
export const ClientLogMessageSchema = z.object({
|
||||
type: z.literal("log"),
|
||||
severity: z.nativeEnum(LogSeverity),
|
||||
severity: z.enum(LogSeverity),
|
||||
log: ID,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { UnitType } from "./game/Game";
|
||||
|
||||
export const BombUnitSchema = z.union([
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import { GameConfigSchema } from "./Schemas";
|
||||
|
||||
export const CreateGameInputSchema = GameConfigSchema.or(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { JWK } from "jose";
|
||||
import { z } from "zod";
|
||||
import { z } from "zod/v4";
|
||||
import {
|
||||
Difficulty,
|
||||
Duos,
|
||||
@@ -98,8 +98,13 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
const jwksUrl = this.jwtIssuer() + "/.well-known/jwks.json";
|
||||
console.log(`Fetching JWKS from ${jwksUrl}`);
|
||||
const response = await fetch(jwksUrl);
|
||||
const jwks = JwksSchema.parse(await response.json());
|
||||
this.publicKey = jwks.keys[0];
|
||||
const result = JwksSchema.safeParse(await response.json());
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.error("Error parsing JWKS", error);
|
||||
throw new Error("Invalid JWKS");
|
||||
}
|
||||
this.publicKey = result.data.keys[0];
|
||||
return this.publicKey;
|
||||
}
|
||||
otelEnabled(): boolean {
|
||||
|
||||
+19
-27
@@ -15,8 +15,8 @@ import {
|
||||
ServerDesyncSchema,
|
||||
ServerErrorMessage,
|
||||
ServerPrestartMessageSchema,
|
||||
ServerStartGameMessageSchema,
|
||||
ServerTurnMessageSchema,
|
||||
ServerStartGameMessage,
|
||||
ServerTurnMessage,
|
||||
Turn,
|
||||
} from "../core/Schemas";
|
||||
import { createGameRecord } from "../core/Util";
|
||||
@@ -327,7 +327,7 @@ export class GameServer {
|
||||
// if no client connects/pings.
|
||||
this.lastPingUpdate = Date.now();
|
||||
|
||||
this.gameStartInfo = GameStartInfoSchema.parse({
|
||||
const result = GameStartInfoSchema.safeParse({
|
||||
gameID: this.id,
|
||||
config: this.gameConfig,
|
||||
players: this.activeClients.map((c) => ({
|
||||
@@ -335,7 +335,13 @@ export class GameServer {
|
||||
clientID: c.clientID,
|
||||
flag: c.flag,
|
||||
})),
|
||||
} satisfies GameStartInfo);
|
||||
});
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
this.log.error("Error parsing game start info", { message: error });
|
||||
return;
|
||||
}
|
||||
this.gameStartInfo = result.data satisfies GameStartInfo;
|
||||
|
||||
this.endTurnIntervalID = setInterval(
|
||||
() => this.endTurn(),
|
||||
@@ -357,13 +363,11 @@ export class GameServer {
|
||||
private sendStartGameMsg(ws: WebSocket, lastTurn: number) {
|
||||
try {
|
||||
ws.send(
|
||||
JSON.stringify(
|
||||
ServerStartGameMessageSchema.parse({
|
||||
type: "start",
|
||||
turns: this.turns.slice(lastTurn),
|
||||
gameStartInfo: this.gameStartInfo,
|
||||
}),
|
||||
),
|
||||
JSON.stringify({
|
||||
type: "start",
|
||||
turns: this.turns.slice(lastTurn),
|
||||
gameStartInfo: this.gameStartInfo,
|
||||
} satisfies ServerStartGameMessage),
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
@@ -386,22 +390,10 @@ export class GameServer {
|
||||
this.handleSynchronization();
|
||||
this.checkDisconnectedStatus();
|
||||
|
||||
let msg = "";
|
||||
try {
|
||||
msg = JSON.stringify(
|
||||
ServerTurnMessageSchema.parse({
|
||||
type: "turn",
|
||||
turn: pastTurn,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
this.log.info(
|
||||
`error sending message for game: ${error.substring(0, 250)}`,
|
||||
{},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const msg = JSON.stringify({
|
||||
type: "turn",
|
||||
turn: pastTurn,
|
||||
} satisfies ServerTurnMessage);
|
||||
this.activeClients.forEach((c) => {
|
||||
c.ws.send(msg);
|
||||
});
|
||||
|
||||
+8
-1
@@ -1,4 +1,5 @@
|
||||
import { jwtVerify } from "jose";
|
||||
import { z } from "zod/v4";
|
||||
import {
|
||||
TokenPayload,
|
||||
TokenPayloadSchema,
|
||||
@@ -32,7 +33,13 @@ export async function verifyClientToken(
|
||||
audience,
|
||||
maxTokenAge: "6 days",
|
||||
});
|
||||
const claims = TokenPayloadSchema.parse(payload);
|
||||
const result = TokenPayloadSchema.safeParse(payload);
|
||||
if (!result.success) {
|
||||
const error = z.prettifyError(result.error);
|
||||
console.warn("Error parsing token payload", error);
|
||||
return false;
|
||||
}
|
||||
const claims = result.data;
|
||||
const persistentId = claims.sub;
|
||||
return { persistentId, claims };
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user