Strongly type Config
Some checks failed
Node.js CI / build (22.x) (push) Has been cancelled

(Also remove a bunch of global types)
This commit is contained in:
Guangcong Luo 2025-05-19 13:27:19 -07:00
parent de13c8c5d4
commit fc6d963912
18 changed files with 75 additions and 53 deletions

View File

@ -1,3 +1,4 @@
/** @type {import('../play.pokemonshowdown.com/src/client-main').PSConfig} */
var Config = Config || {}; var Config = Config || {};
/* version */ Config.version = "0"; /* version */ Config.version = "0";

View File

@ -10,6 +10,7 @@
*/ */
import { type AnimTable, BattleOtherAnims } from './battle-animations'; import { type AnimTable, BattleOtherAnims } from './battle-animations';
import { Config } from './client-main';
export const BattleMoveAnims: AnimTable = { export const BattleMoveAnims: AnimTable = {
taunt: { taunt: {

View File

@ -25,6 +25,7 @@ import {
} from "./battle-dex-data"; } from "./battle-dex-data";
import type * as DexData from "./battle-dex-data"; import type * as DexData from "./battle-dex-data";
import type { Teams } from "./battle-teams"; import type { Teams } from "./battle-teams";
import { Config } from "./client-main";
export declare namespace Dex { export declare namespace Dex {
/* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable @typescript-eslint/no-shadow */
@ -290,9 +291,9 @@ export const Dex = new class implements ModdedDex {
} }
if (avatar.includes('.') && window.Config?.server?.registered) { if (avatar.includes('.') && window.Config?.server?.registered) {
// custom avatar served by the server // custom avatar served by the server
let protocol = (Config.server.port === 443) ? 'https' : 'http'; const protocol = (Config.server.port === 443) ? 'https' : 'http';
return protocol + '://' + Config.server.host + ':' + Config.server.port + const server = `${protocol}://${Config.server.host}:${Config.server.port}`;
'/avatars/' + encodeURIComponent(avatar).replace(/%3F/g, '?'); return `${server}/avatars/${encodeURIComponent(avatar).replace(/%3F/g, '?')}`;
} }
return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png'; return Dex.resourcePrefix + 'sprites/trainers/' + Dex.sanitizeName(avatar || 'unknown') + '.png';
} }
@ -888,7 +889,7 @@ export const Dex = new class implements ModdedDex {
getItemIcon(item: any) { getItemIcon(item: any) {
let num = 0; let num = 0;
if (typeof item === 'string' && exports.BattleItems) item = exports.BattleItems[toID(item)]; if (typeof item === 'string' && window.BattleItems) item = window.BattleItems[toID(item)];
if (item?.spritenum) num = item.spritenum; if (item?.spritenum) num = item.spritenum;
let top = Math.floor(num / 16) * 24; let top = Math.floor(num / 16) * 24;
@ -922,8 +923,8 @@ export const Dex = new class implements ModdedDex {
getPokeballs() { getPokeballs() {
if (this.pokeballs) return this.pokeballs; if (this.pokeballs) return this.pokeballs;
this.pokeballs = []; this.pokeballs = [];
if (!window.BattleItems) window.BattleItems = {}; window.BattleItems ||= {};
for (const data of Object.values<AnyObject>(window.BattleItems)) { for (const data of Object.values(BattleItems)) {
if (!data.isPokeball) continue; if (!data.isPokeball) continue;
this.pokeballs.push(data.name); this.pokeballs.push(data.name);
} }
@ -944,7 +945,7 @@ export class ModdedDex {
pokeballs: string[] | null = null; pokeballs: string[] | null = null;
constructor(modid: ID) { constructor(modid: ID) {
this.modid = modid; this.modid = modid;
const gen = parseInt(modid.substr(3, 1), 10); const gen = parseInt(modid.charAt(3), 10);
if (!modid.startsWith('gen') || !gen) throw new Error("Unsupported modid"); if (!modid.startsWith('gen') || !gen) throw new Error("Unsupported modid");
this.gen = gen; this.gen = gen;
} }
@ -1132,8 +1133,8 @@ export class ModdedDex {
getPokeballs() { getPokeballs() {
if (this.pokeballs) return this.pokeballs; if (this.pokeballs) return this.pokeballs;
this.pokeballs = []; this.pokeballs = [];
if (!window.BattleItems) window.BattleItems = {}; window.BattleItems ||= {};
for (const data of Object.values<AnyObject>(window.BattleItems)) { for (const data of Object.values(BattleItems)) {
if (data.gen && data.gen > this.gen) continue; if (data.gen && data.gen > this.gen) continue;
if (!data.isPokeball) continue; if (!data.isPokeball) continue;
this.pokeballs.push(data.name); this.pokeballs.push(data.name);

View File

@ -19,6 +19,7 @@ import { Dex, toID, toRoomid, toUserid, type ID } from './battle-dex';
import { Teams } from './battle-teams'; import { Teams } from './battle-teams';
import { BattleTextParser, type Args, type KWArgs } from './battle-text-parser'; import { BattleTextParser, type Args, type KWArgs } from './battle-text-parser';
import { Net } from './client-connection'; // optional import { Net } from './client-connection'; // optional
import { Config } from './client-main';
// Caja // Caja
declare const html4: any; declare const html4: any;
@ -298,7 +299,7 @@ export class BattleLog {
const body = args[2]; const body = args[2];
const roomid = this.scene?.battle.roomid; const roomid = this.scene?.battle.roomid;
if (!roomid) break; if (!roomid) break;
app.rooms[roomid].notifyOnce(title, body, 'highlight'); window.app?.rooms[roomid].notifyOnce(title, body, 'highlight');
break; break;
case 'showteam': { case 'showteam': {
@ -1344,7 +1345,7 @@ export class BattleLog {
} }
static interstice = (() => { static interstice = (() => {
const whitelist: string[] = Config.whitelist; const whitelist = Config.whitelist || [];
const patterns = whitelist.map(entry => new RegExp( const patterns = whitelist.map(entry => new RegExp(
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry.replace(/\./g, '\\.')}(/.*)?`, 'i' `^(https?:)?//([A-Za-z0-9-]*\\.)?${entry.replace(/\./g, '\\.')}(/.*)?`, 'i'
)); ));

View File

@ -10,6 +10,7 @@
import preact from "../js/lib/preact"; import preact from "../js/lib/preact";
import { Dex, toID, type ID } from "./battle-dex"; import { Dex, toID, type ID } from "./battle-dex";
import type { DexSearch, SearchRow, SearchType } from "./battle-dex-search"; import type { DexSearch, SearchRow, SearchType } from "./battle-dex-search";
import { Config } from "./client-main";
export class PSSearchResults extends preact.Component<{ export class PSSearchResults extends preact.Component<{
search: DexSearch, windowing?: number | null, hideFilters?: boolean, firstRow?: SearchRow, search: DexSearch, windowing?: number | null, hideFilters?: boolean, firstRow?: SearchRow,

View File

@ -1,4 +1,4 @@
import { PS } from "./client-main"; import { Config, PS } from "./client-main";
export class BattleBGM { export class BattleBGM {
/** /**
@ -116,7 +116,7 @@ export const BattleSound = new class {
if (this.soundCache[url]) return this.soundCache[url]; if (this.soundCache[url]) return this.soundCache[url];
try { try {
const sound = document.createElement('audio'); const sound = document.createElement('audio');
sound.src = 'https://' + Config.routes.client + '/' + url; sound.src = `https://${Config.routes.client}/${url}`;
sound.volume = this.effectVolume / 100; sound.volume = this.effectVolume / 100;
this.soundCache[url] = sound; this.soundCache[url] = sound;
return sound; return sound;

View File

@ -5,7 +5,7 @@
* @license MIT * @license MIT
*/ */
import { PS } from "./client-main"; import { Config, PS } from "./client-main";
declare const SockJS: any; declare const SockJS: any;
declare const POKEMON_SHOWDOWN_TESTCLIENT_KEY: string | undefined; declare const POKEMON_SHOWDOWN_TESTCLIENT_KEY: string | undefined;
@ -26,8 +26,8 @@ export class PSConnection {
} }
connect() { connect() {
const server = PS.server; const server = PS.server;
const port = server.protocol === 'https' ? ':' + server.port : ':' + server.httpport; const port = server.protocol === 'https' ? `:${server.port}` : `:${server.httpport || server.port}`;
const url = server.protocol + '://' + server.host + port + server.prefix; const url = `${server.protocol}://${server.host}${port}${server.prefix}`;
try { try {
this.socket = new SockJS(url, [], { timeout: 5 * 60 * 1000 }); this.socket = new SockJS(url, [], { timeout: 5 * 60 * 1000 });
} catch { } catch {

View File

@ -13,7 +13,7 @@
* @license AGPLv3 * @license AGPLv3
*/ */
import { PS } from "./client-main"; import { Config, PS } from "./client-main";
declare const ColorThief: any; declare const ColorThief: any;
/********************************************************************** /**********************************************************************

View File

@ -22,6 +22,38 @@ import { Teams } from './battle-teams';
declare const BattleTextAFD: any; declare const BattleTextAFD: any;
declare const BattleTextNotAFD: any; declare const BattleTextNotAFD: any;
/**********************************************************************
* Config
*********************************************************************/
export interface ServerInfo {
id: ID;
protocol: string;
host: string;
port: number;
httpport?: number;
altport?: number;
prefix: string;
afd?: boolean;
registered?: boolean;
}
export interface PSConfig {
server: ServerInfo;
defaultserver: ServerInfo;
routes: {
root: string,
client: string,
dex: string,
replays: string,
users: string,
teams: string,
};
customcolors: Record<string, string>;
whitelist?: string[];
testclient?: boolean;
}
export declare const Config: PSConfig;
/********************************************************************** /**********************************************************************
* Prefs * Prefs
*********************************************************************/ *********************************************************************/

View File

@ -3,42 +3,22 @@
// dex data // dex data
/////////// ///////////
type AnyObject = { [k: string]: any };
declare const BattleText: { [id: string]: { [templateName: string]: string } }; declare const BattleText: { [id: string]: { [templateName: string]: string } };
declare const BattleFormats: { [id: string]: import('./panel-teamdropdown').FormatData }; declare const BattleFormats: { [id: string]: import('./panel-teamdropdown').FormatData };
declare const BattlePokedex: any; declare const BattlePokedex: { [id: string]: AnyObject };
declare const BattleMovedex: any; declare const BattleMovedex: { [id: string]: AnyObject };
declare const BattleAbilities: any; declare const BattleAbilities: { [id: string]: AnyObject };
declare const BattleItems: any; declare const BattleItems: { [id: string]: AnyObject };
declare const BattleAliases: any; declare const BattleAliases: { [id: string]: string };
declare const BattleStatuses: any; declare const BattleStatuses: { [id: string]: AnyObject };
declare const BattlePokemonSprites: any; declare const BattlePokemonSprites: { [id: string]: AnyObject };
declare const BattlePokemonSpritesBW: any; declare const BattlePokemonSpritesBW: { [id: string]: AnyObject };
declare const NonBattleGames: { [id: string]: string }; declare const NonBattleGames: { [id: string]: string };
// PS globals // Window
///////////// /////////
declare const Config: any;
declare const Replays: any;
declare const exports: any;
type AnyObject = { [k: string]: any };
declare const app: { user: AnyObject, rooms: AnyObject, ignore?: AnyObject };
interface Window { interface Window {
[k: string]: any; [k: string]: any;
} }
// Temporary globals (exported from modules, used by non-module files)
// When creating new module files, these should all be commented out
// to make sure they're not being used globally in modules.
// declare var Battle: typeof import('./battle').Battle;
// type Battle = import('./battle').Battle;
// declare var BattleScene: typeof import('./battle-animations').BattleScene;
// type BattleScene = import('./battle-animations').BattleScene;
// declare var Pokemon: typeof import('./battle').Pokemon;
// type Pokemon = import('./battle').Pokemon;
// type ServerPokemon = import('./battle').ServerPokemon;
// declare var BattleLog: typeof import('./battle-log').BattleLog;
// type BattleLog = import('./battle-log').BattleLog;

View File

@ -6,7 +6,7 @@
*/ */
import preact from "../js/lib/preact"; import preact from "../js/lib/preact";
import { PS, PSRoom, type RoomOptions, type RoomID } from "./client-main"; import { PS, PSRoom, type RoomOptions, type RoomID, Config } from "./client-main";
import { PSIcon, PSPanelWrapper, PSRoomPanel } from "./panels"; import { PSIcon, PSPanelWrapper, PSRoomPanel } from "./panels";
import { ChatLog, ChatRoom, ChatTextEntry, ChatUserList } from "./panel-chat"; import { ChatLog, ChatRoom, ChatTextEntry, ChatUserList } from "./panel-chat";
import { FormatDropdown } from "./panel-mainmenu"; import { FormatDropdown } from "./panel-mainmenu";

View File

@ -7,7 +7,7 @@
* @license MIT * @license MIT
*/ */
import { PS, PSRoom } from "./client-main"; import { Config, PS, PSRoom } from "./client-main";
import { Net } from "./client-connection"; import { Net } from "./client-connection";
import { PSPanelWrapper, PSRoomPanel } from "./panels"; import { PSPanelWrapper, PSRoomPanel } from "./panels";
import { BattleLog } from "./battle-log"; import { BattleLog } from "./battle-log";

View File

@ -7,7 +7,7 @@
import preact from "../js/lib/preact"; import preact from "../js/lib/preact";
import { PSLoginServer } from "./client-connection"; import { PSLoginServer } from "./client-connection";
import { PS, PSRoom, type RoomID, type RoomOptions, type Team } from "./client-main"; import { Config, PS, PSRoom, type RoomID, type RoomOptions, type Team } from "./client-main";
import { PSIcon, PSPanelWrapper, PSRoomPanel } from "./panels"; import { PSIcon, PSPanelWrapper, PSRoomPanel } from "./panels";
import type { BattlesRoom } from "./panel-battle"; import type { BattlesRoom } from "./panel-battle";
import type { ChatRoom } from "./panel-chat"; import type { ChatRoom } from "./panel-chat";

View File

@ -4,7 +4,9 @@ import type { ID } from "./battle-dex-data";
import { BattleLog } from "./battle-log"; import { BattleLog } from "./battle-log";
import { PSLoginServer } from "./client-connection"; import { PSLoginServer } from "./client-connection";
import { PSBackground } from "./client-core"; import { PSBackground } from "./client-core";
import { PSRoom, type RoomOptions, PS, type PSLoginState, type RoomID, type TimestampOptions } from "./client-main"; import {
PS, PSRoom, Config, type RoomOptions, type PSLoginState, type RoomID, type TimestampOptions,
} from "./client-main";
import { type BattleRoom } from "./panel-battle"; import { type BattleRoom } from "./panel-battle";
import { ChatUserList, type ChatRoom } from "./panel-chat"; import { ChatUserList, type ChatRoom } from "./panel-chat";
import { PSRoomPanel, PSPanelWrapper, PSView } from "./panels"; import { PSRoomPanel, PSPanelWrapper, PSView } from "./panels";

View File

@ -10,7 +10,7 @@
*/ */
import preact from "../js/lib/preact"; import preact from "../js/lib/preact";
import { PS, type PSRoom, type RoomID } from "./client-main"; import { Config, PS, type PSRoom, type RoomID } from "./client-main";
import { PSView } from "./panels"; import { PSView } from "./panels";
import type { Battle } from "./battle"; import type { Battle } from "./battle";
import { BattleLog } from "./battle-log"; // optional import { BattleLog } from "./battle-log"; // optional

View File

@ -3,6 +3,7 @@
import preact from '../../play.pokemonshowdown.com/js/lib/preact'; import preact from '../../play.pokemonshowdown.com/js/lib/preact';
import { Net, MiniTeam, type ServerTeam } from './utils'; import { Net, MiniTeam, type ServerTeam } from './utils';
import type { PageProps } from './teams'; import type { PageProps } from './teams';
import { Config } from '../../play.pokemonshowdown.com/src/client-main';
declare const toID: (val: any) => string; declare const toID: (val: any) => string;

View File

@ -3,6 +3,7 @@
import preact from '../../play.pokemonshowdown.com/js/lib/preact'; import preact from '../../play.pokemonshowdown.com/js/lib/preact';
import { Net, type ServerTeam, MiniTeam } from './utils'; import { Net, type ServerTeam, MiniTeam } from './utils';
import type { PageProps } from './teams'; import type { PageProps } from './teams';
import { Config } from '../../play.pokemonshowdown.com/src/client-main';
declare const toID: (val: any) => string; declare const toID: (val: any) => string;
declare const BattleAliases: Record<string, string>; declare const BattleAliases: Record<string, string>;

View File

@ -6,6 +6,7 @@ import { BattleLog } from '../../play.pokemonshowdown.com/src/battle-log';
import type { PageProps } from './teams'; import type { PageProps } from './teams';
import { Dex } from '../../play.pokemonshowdown.com/src/battle-dex'; import { Dex } from '../../play.pokemonshowdown.com/src/battle-dex';
import { BattleStatNames } from '../../play.pokemonshowdown.com/src/battle-dex-data'; import { BattleStatNames } from '../../play.pokemonshowdown.com/src/battle-dex-data';
import { Config } from '../../play.pokemonshowdown.com/src/client-main';
declare const toID: (str: any) => string; declare const toID: (str: any) => string;
declare const BattleAliases: Record<string, string>; declare const BattleAliases: Record<string, string>;