diff --git a/src/aimedb/db.ts b/src/aimedb/db.ts index 9d48648..dcb5365 100644 --- a/src/aimedb/db.ts +++ b/src/aimedb/db.ts @@ -30,7 +30,7 @@ class CardRepositoryImpl implements CardRepository { await this._txn.modify(touchSql); - return extId; + return parseInt(extId, 10) as AimeId; } async register(luid: string, now: Date): Promise { diff --git a/src/idz/db/backgrounds.ts b/src/idz/db/backgrounds.ts index 23e22df..c48e77e 100644 --- a/src/idz/db/backgrounds.ts +++ b/src/idz/db/backgrounds.ts @@ -20,7 +20,7 @@ export class SqlBackgroundsRepository const result = new Set(); for (const row of rows) { - result.add(row.background_no); + result.add(parseInt(row.background_no) as BackgroundCode); } return result; diff --git a/src/idz/db/car.ts b/src/idz/db/car.ts index 017ebdb..fe8b856 100644 --- a/src/idz/db/car.ts +++ b/src/idz/db/car.ts @@ -3,25 +3,25 @@ import sql from "sql-bricks-postgres"; import { Car, CarSelector } from "../model/car"; import { Profile } from "../model/profile"; import { CarRepository } from "../repo"; -import { Id, Transaction, generateId } from "../../sql"; +import { Id, Row, Transaction, generateId } from "../../sql"; -function _extractRow(row: any): Car { +function _extractRow(row: Row): Car { return { - selector: row.selector, - field_00: row.field_00, - field_02: row.field_02, - field_04: row.field_04.split(",").map((x: string) => parseInt(x, 10)), - field_46: row.field_46, - field_48: row.field_48, - field_4a: row.field_4a, - field_4c: row.field_4c, - field_50_lo: row.field_50_lo, - field_50_hi: row.field_50_hi, - field_58: row.field_58, - field_5a: row.field_5a, - field_5b: row.field_5b, - field_5c: row.field_5c, - field_5e: row.field_5e, + selector: parseInt(row.selector) as CarSelector, + field_00: parseInt(row.field_00), + field_02: parseInt(row.field_02), + field_04: row.field_04.split(",").map((x: string) => parseInt(x)), + field_46: parseInt(row.field_46), + field_48: parseInt(row.field_48), + field_4a: parseInt(row.field_4a), + field_4c: parseInt(row.field_4c), + field_50_lo: parseInt(row.field_50_lo), + field_50_hi: parseInt(row.field_50_hi), + field_58: parseInt(row.field_58), + field_5a: parseInt(row.field_5a), + field_5b: parseInt(row.field_5b), + field_5c: parseInt(row.field_5c), + field_5e: parseInt(row.field_5e), }; } @@ -36,7 +36,7 @@ export class SqlCarRepository implements CarRepository { const row = await this._txn.fetchRow(countSql); - return parseInt(row!.result, 10); + return parseInt(row!.result); } async loadAllCars(profileId: Id): Promise { @@ -59,6 +59,10 @@ export class SqlCarRepository implements CarRepository { const row = await this._txn.fetchRow(loadSql); + if (row === undefined) { + throw new Error(`Car selection not found, profileId=${profileId}`); + } + return _extractRow(row); } diff --git a/src/idz/db/chara.ts b/src/idz/db/chara.ts index 341dc7f..a3ec589 100644 --- a/src/idz/db/chara.ts +++ b/src/idz/db/chara.ts @@ -1,22 +1,23 @@ import sql from "sql-bricks-postgres"; -import { Chara } from "../model/chara"; +import { BackgroundCode, TitleCode } from "../model/base"; +import { Chara, Gender } from "../model/chara"; import { Profile } from "../model/profile"; import { FacetRepository } from "../repo"; -import { Id, Transaction } from "../../sql"; +import { Id, Row, Transaction } from "../../sql"; -export function _extractChara(row: any): Chara { +export function _extractChara(row: Row): Chara { return { - gender: row.gender, - field_02: row.field_02, - field_04: row.field_04, - field_06: row.field_06, - field_08: row.field_08, - field_0a: row.field_0a, - field_0c: row.field_0c, - field_0e: row.field_0e, - title: row.title, - background: row.background, + gender: row.gender as Gender, + field_02: parseInt(row.field_02), + field_04: parseInt(row.field_04), + field_06: parseInt(row.field_06), + field_08: parseInt(row.field_08), + field_0a: parseInt(row.field_0a), + field_0c: parseInt(row.field_0c), + field_0e: parseInt(row.field_0e), + title: parseInt(row.title) as TitleCode, + background: parseInt(row.background) as BackgroundCode, }; } @@ -31,6 +32,10 @@ export class SqlCharaRepository implements FacetRepository { const row = await this._txn.fetchRow(loadSql); + if (row === undefined) { + throw new Error(`Chara not found: profileId=${profileId}`); + } + return _extractChara(row); } diff --git a/src/idz/db/coursePlays.ts b/src/idz/db/coursePlays.ts index f4ce414..c630d98 100644 --- a/src/idz/db/coursePlays.ts +++ b/src/idz/db/coursePlays.ts @@ -15,10 +15,13 @@ export class SqlCoursePlaysRepository implements CoursePlaysRepository { .where("cp.profile_id", profileId); const rows = await this._txn.fetchRows(loadSql); - const result = new Map(); + const result = new Map(); for (const row of rows) { - result.set(row.course_no, row.count); + const courseNo = parseInt(row.course_no) as CourseNo; + const count = parseInt(row.count); + + result.set(courseNo, count); } return result; diff --git a/src/idz/db/missions.ts b/src/idz/db/missions.ts index 44ccb08..e082c41 100644 --- a/src/idz/db/missions.ts +++ b/src/idz/db/missions.ts @@ -35,7 +35,11 @@ export class SqlMissionsRepository implements FacetRepository { const rows = await this._txn.fetchRows(loadSoloSql); for (const row of rows) { - result.solo[row.grid_no].cells[row.cell_no] = row.value; + const gridNo = parseInt(row.grid_no); + const cellNo = parseInt(row.cell_no); + const value = parseInt(row.value); + + result.solo[gridNo].cells[cellNo] = value; } return result; diff --git a/src/idz/db/profile.ts b/src/idz/db/profile.ts index cb6bd6a..0848638 100644 --- a/src/idz/db/profile.ts +++ b/src/idz/db/profile.ts @@ -3,19 +3,19 @@ import sql from "sql-bricks-postgres"; import { Profile } from "../model/profile"; import { ProfileRepository } from "../repo"; import { AimeId } from "../../model"; -import { Id, Transaction, generateId } from "../../sql"; +import { Id, Row, Transaction, generateId } from "../../sql"; -export function _extractProfile(row: any): Profile { +export function _extractProfile(row: Row): Profile { return { - aimeId: row.aime_id, + aimeId: parseInt(row.aime_id) as AimeId, name: row.name, - lv: row.lv, - exp: row.exp, - fame: row.fame, - dpoint: row.dpoint, - mileage: row.mileage, - accessTime: row.access_time, - registerTime: row.register_time, + lv: parseInt(row.lv), + exp: parseInt(row.exp), + fame: parseInt(row.fame), + dpoint: parseInt(row.dpoint), + mileage: parseInt(row.mileage), + accessTime: new Date(row.access_time), + registerTime: new Date(row.register_time), }; } @@ -45,7 +45,7 @@ export class SqlProfileRepository implements ProfileRepository { return undefined; } - return row.id; + return BigInt(row.id) as Id; } async load(id: Id): Promise { @@ -57,6 +57,10 @@ export class SqlProfileRepository implements ProfileRepository { const row = await this._txn.fetchRow(loadSql); + if (row === undefined) { + throw new Error(`Profile not found, id=${id}`); + } + return _extractProfile(row); } @@ -68,7 +72,7 @@ export class SqlProfileRepository implements ProfileRepository { fame: profile.fame, dpoint: profile.dpoint, mileage: profile.mileage, - access_time: profile.accessTime, + access_time: profile.accessTime.toISOString(), }) .where("id", id); @@ -99,8 +103,8 @@ export class SqlProfileRepository implements ProfileRepository { fame: profile.fame, dpoint: profile.dpoint, mileage: profile.mileage, - register_time: profile.registerTime, - access_time: profile.accessTime, + register_time: profile.registerTime.toISOString(), + access_time: profile.accessTime.toISOString(), }); await this._txn.modify(createSql); diff --git a/src/idz/db/settings.ts b/src/idz/db/settings.ts index 5d95217..7ef210e 100644 --- a/src/idz/db/settings.ts +++ b/src/idz/db/settings.ts @@ -21,10 +21,10 @@ export class SqlSettingsRepository implements FacetRepository { } return { - music: row.music, - pack: parseInt(row.pack, 10), - paperCup: row.paper_cup, - gauges: row.gauges, + music: parseInt(row.music), + pack: parseInt(row.pack), + paperCup: parseInt(row.paper_cup), + gauges: parseInt(row.gauges), }; } diff --git a/src/idz/db/story.ts b/src/idz/db/story.ts index b8e62db..b585ac9 100644 --- a/src/idz/db/story.ts +++ b/src/idz/db/story.ts @@ -19,8 +19,8 @@ export class SqlStoryRepository implements FacetRepository { // Must succeed even if nonexistent (required by save method below) const result = { - x: header !== undefined ? header.x : 0, - y: header !== undefined ? header.y : 0, + x: header !== undefined ? parseInt(header.x) : 0, + y: header !== undefined ? parseInt(header.y) : 0, rows: new Array(), }; @@ -42,10 +42,12 @@ export class SqlStoryRepository implements FacetRepository { const rows = await this._txn.fetchRows(loadCellSql); for (const row of rows) { - const cell = result.rows[row.row_no].cells[row.col_no]; + const rowNo = parseInt(row.row_no); + const colNo = parseInt(row.col_no); + const cell = result.rows[rowNo].cells[colNo]; - cell.a = row.a; - cell.b = row.b; + cell.a = parseInt(row.a); + cell.b = parseInt(row.b); } return result; diff --git a/src/idz/db/team.ts b/src/idz/db/team.ts index 0fc5502..c579d71 100644 --- a/src/idz/db/team.ts +++ b/src/idz/db/team.ts @@ -21,7 +21,7 @@ export class SqlTeamRepository implements TeamRepository { throw new Error(`Team not found for ExtID ${extId}`); } - return row.id; + return BigInt(row.id) as Id; } async load(id: Id): Promise { @@ -37,10 +37,10 @@ export class SqlTeamRepository implements TeamRepository { } return { - extId: row.ext_id, + extId: parseInt(row.ext_id) as ExtId, name: row.name, - nameBg: row.name_bg, - nameFx: row.name_fx, + nameBg: parseInt(row.name_bg), + nameFx: parseInt(row.name_fx), registerTime: new Date(row.register_time), }; } diff --git a/src/idz/db/teamAuto.ts b/src/idz/db/teamAuto.ts index 943d6a3..3945da4 100644 --- a/src/idz/db/teamAuto.ts +++ b/src/idz/db/teamAuto.ts @@ -19,10 +19,10 @@ export class SqlTeamAutoRepository implements TeamAutoRepository { return ( row && [ { - serialNo: row.serial_no, - nameIdx: row.name_idx, + serialNo: parseInt(row.serial_no), + nameIdx: parseInt(row.name_idx), }, - row.id, + BigInt(row.id) as Id, ] ); } diff --git a/src/idz/db/teamMember.ts b/src/idz/db/teamMember.ts index 532c29a..4bc8f92 100644 --- a/src/idz/db/teamMember.ts +++ b/src/idz/db/teamMember.ts @@ -22,7 +22,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { return undefined; } - return row.team_id; + return BigInt(row.team_id) as Id; } async findLeader(teamId: Id): Promise | undefined> { @@ -38,7 +38,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { return undefined; } - return row.id; + return BigInt(row.id) as Id; } async loadRoster(teamId: Id): Promise { @@ -52,10 +52,10 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { const rows = await this._txn.fetchRows(loadSql); - return rows.map((row: any) => ({ + return rows.map(row => ({ profile: _extractProfile(row), chara: _extractChara(row), - leader: row.leader, + leader: !!row.leader, joinTime: new Date(row.join_time), })); } @@ -91,7 +91,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { const row = await this._txn.fetchRow(countSql); - if (row!.count >= 6) { + if (parseInt(row!.count) >= 6) { throw new Error(`Team ${teamId} is full`); } diff --git a/src/idz/db/teamReservation.ts b/src/idz/db/teamReservation.ts index 8e96406..0e414b3 100644 --- a/src/idz/db/teamReservation.ts +++ b/src/idz/db/teamReservation.ts @@ -22,16 +22,13 @@ export class SqlTeamReservationRepository async occupancyHack(teamId: Id): Promise { await this._lockTeam(teamId); - // counts get returned as strings, so 1 + 0 = 10. - // it hardly needs to be said but fuck javascript. - const memberSql = sql .select("count(*) as count") .from("idz_team_member tm") .where("tm.team_id", teamId); const memberRes = await this._txn.fetchRow(memberSql); - const memberCount = parseInt(memberRes!.count, 10); + const memberCount = parseInt(memberRes!.count); const reservSql = sql .select("count(*) as count") @@ -39,7 +36,7 @@ export class SqlTeamReservationRepository .where("tr.team_id", teamId); const reservRes = await this._txn.fetchRow(reservSql); - const reservCount = parseInt(reservRes!.count, 10); + const reservCount = parseInt(reservRes!.count); return memberCount + reservCount; } diff --git a/src/idz/db/timeAttack.ts b/src/idz/db/timeAttack.ts index c88b1c9..b9e9f17 100644 --- a/src/idz/db/timeAttack.ts +++ b/src/idz/db/timeAttack.ts @@ -1,10 +1,23 @@ import sql from "sql-bricks-postgres"; import { RouteNo } from "../model/base"; +import { CarSelector } from "../model/car"; import { Profile } from "../model/profile"; import { TimeAttackScore } from "../model/timeAttack"; import { TimeAttackRepository, TopTenResult } from "../repo"; -import { Id, Transaction, generateId } from "../../sql"; +import { Id, Row, Transaction, generateId } from "../../sql"; + +function _extractRow(row: Row): TimeAttackScore { + return { + routeNo: parseInt(row.route_no) as RouteNo, + timestamp: new Date(row.timestamp), + flags: parseInt(row.flags), + totalTime: parseFloat(row.total_time), + sectionTimes: row.section_times.split(",").map(parseFloat), + grade: parseInt(row.grade), + carSelector: parseInt(row.car_selector) as CarSelector, + }; +} export class SqlTimeAttackRepository implements TimeAttackRepository { constructor(private readonly _txn: Transaction) {} @@ -26,15 +39,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { return rows.map(row => ({ driverName: row.name, - ta: { - routeNo: row.route_no, - timestamp: new Date(row.timestamp), - flags: row.flags, - totalTime: row.total_time, - sectionTimes: row.section_times.split(","), - grade: row.grade, - carSelector: row.car_selector, - }, + ta: _extractRow(row), })); } @@ -46,15 +51,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { const rows = await this._txn.fetchRows(loadSql); - return rows.map(row => ({ - routeNo: row.route_no, - timestamp: new Date(row.timestamp), - flags: row.flags, - totalTime: row.total_time, - sectionTimes: row.section_times.split(",").map(parseFloat), - grade: row.grade, - carSelector: row.car_selector, - })); + return rows.map(_extractRow); } async save(profileId: Id, score: TimeAttackScore): Promise { @@ -94,20 +91,22 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { }); await this._txn.modify(insertSql); - } else if (score.totalTime < row.total_time) { - const updateSql = sql - .update("idz_ta_best", { - total_time: score.totalTime, - section_times: score.sectionTimes.join(","), - flags: score.flags, - grade: score.grade, - car_selector: score.carSelector, - timestamp: score.timestamp, - }) - .where("profile_id", profileId) - .where("route_no", score.routeNo); + } else { + if (score.totalTime < parseFloat(row.total_time)) { + const updateSql = sql + .update("idz_ta_best", { + total_time: score.totalTime, + section_times: score.sectionTimes.join(","), + flags: score.flags, + grade: score.grade, + car_selector: score.carSelector, + timestamp: score.timestamp, + }) + .where("profile_id", profileId) + .where("route_no", score.routeNo); - await this._txn.modify(updateSql); + await this._txn.modify(updateSql); + } } } } diff --git a/src/idz/db/titles.ts b/src/idz/db/titles.ts index 81e478c..5bc9550 100644 --- a/src/idz/db/titles.ts +++ b/src/idz/db/titles.ts @@ -18,7 +18,7 @@ export class SqlTitlesRepository implements FlagRepository { const result = new Set(); for (const row of rows) { - result.add(row.title_no); + result.add(parseInt(row.title_no) as TitleCode); } return result; diff --git a/src/idz/db/unlocks.ts b/src/idz/db/unlocks.ts index 363911b..d3e30f2 100644 --- a/src/idz/db/unlocks.ts +++ b/src/idz/db/unlocks.ts @@ -21,10 +21,10 @@ export class SqlUnlocksRepository implements FacetRepository { } return { - cup: row.cup, - gauges: row.gauges, - music: row.music, - lastMileageReward: row.last_mileage_reward, + cup: parseInt(row.cup), + gauges: parseInt(row.gauges), + music: parseInt(row.music), + lastMileageReward: parseInt(row.last_mileage_reward), }; } diff --git a/src/idz/model/chara.ts b/src/idz/model/chara.ts index 7a96cba..e2e9c9e 100644 --- a/src/idz/model/chara.ts +++ b/src/idz/model/chara.ts @@ -1,7 +1,9 @@ import { BackgroundCode, TitleCode } from "./base"; +export type Gender = "male" | "female"; + export interface Chara { - gender: "male" | "female"; + gender: Gender; field_02: number; field_04: number; field_06: number; diff --git a/src/sql/api.ts b/src/sql/api.ts index 2dc8d4a..7c08c79 100644 --- a/src/sql/api.ts +++ b/src/sql/api.ts @@ -3,7 +3,7 @@ import * as sql from "sql-bricks-postgres"; export type Id = bigint & { __id: T }; export interface Row { - [key: string]: any; + [key: string]: string; } export interface Transaction {