feat(api): ship + research endpoints queue tasks with lead + helpers; mission accept is instant and free (#44)
CI / build + unit + e2e (pull_request) Failing after 3m58s

This commit is contained in:
2026-10-02 18:36:14 +00:00
parent 4e312d054f
commit 03b8b661f6
20 changed files with 772 additions and 103 deletions
+36
View File
@@ -131,6 +131,19 @@ export async function resolveShipBuildTask(db: GameDb, task: Task): Promise<void
await persist(db)
}
// The upgrade pre-checks, extracted for the queueing route (5a(ii)): the same codes and
// messages as upgradeSystem — unknown system (404), not built (409), at max level (409).
// Cost, turn and the level-up effect stay out: the queueing path deducts at queue time
// (queueTask) and the effect lands when the task completes on the tick.
export async function checkUpgradePreconditions(db: GameDb, systemId: number): Promise<void> {
await ensureSave(db)
const target = (await db.db.select().from(schema.shipSystems)).find((s) => s.id === systemId)
if (!target) throw new ShipError(404, `Unknown ship system ${systemId}`)
if (target.level <= 0) throw new ShipError(409, `${target.type} is not built`)
const maxLevel = effectiveLevelCap(target.type, await researchedTechSlugs(db))
if (target.level >= maxLevel) throw new ShipError(409, `${target.type} is at max level (${maxLevel})`)
}
// UPGRADE — level up a built system by one (capped at the effective max level, raised by techs).
export async function upgradeSystem(db: GameDb, systemId: number): Promise<ShipState> {
await ensureSave(db)
@@ -148,6 +161,17 @@ export async function upgradeSystem(db: GameDb, systemId: number): Promise<ShipS
return readShipState(db)
}
// The expand pre-checks, extracted for the queueing route (5a(ii)): the same codes and
// messages as expandSystem — unknown system (404), not built (409), at max capacity (409).
export async function checkExpandPreconditions(db: GameDb, systemId: number): Promise<void> {
await ensureSave(db)
const target = (await db.db.select().from(schema.shipSystems)).find((s) => s.id === systemId)
if (!target) throw new ShipError(404, `Unknown ship system ${systemId}`)
if (target.level <= 0) throw new ShipError(409, `${target.type} is not built`)
const maxCapacity = effectiveCapacityCap(target.type, await researchedTechSlugs(db))
if (target.capacity >= maxCapacity) throw new ShipError(409, `${target.type} is at max capacity (${maxCapacity})`)
}
// EXPAND — raise a built system's capacity by one (capped at the effective max capacity, raised by techs).
export async function expandSystem(db: GameDb, systemId: number): Promise<ShipState> {
await ensureSave(db)
@@ -165,6 +189,18 @@ export async function expandSystem(db: GameDb, systemId: number): Promise<ShipSt
return readShipState(db)
}
// The build pre-checks, extracted for the queueing route (5a(ii)): the same codes and
// messages as buildSystem — unknown type (404), already built (409), build-gate tech
// missing (409). The parts cost stays out: the queueing path deducts it at queue time.
export async function checkBuildPreconditions(db: GameDb, type: string): Promise<void> {
await ensureSave(db)
const target = (await db.db.select().from(schema.shipSystems)).find((s) => s.type === type)
if (!target) throw new ShipError(404, `Unknown ship system type ${type}`)
if (target.level > 0) throw new ShipError(409, `${target.type} is already built`)
const gate = buildGate(type, await researchedTechSlugs(db))
if (gate) throw new ShipError(409, `Building ${type} requires the ${gate} tech`)
}
// BUILD — build an unbuilt type at base values. Cost: a flat amount of parts.
export async function buildSystem(db: GameDb, type: string): Promise<ShipState> {
await ensureSave(db)