v0.5.0 — CREW: roster with good/bad traits (#7) #14

Merged
kamilkrzywda merged 6 commits from crew-roster into master 2026-09-29 01:03:25 +02:00
3 changed files with 57 additions and 1 deletions
Showing only changes of commit a2c8586618 - Show all commits
+8
View File
@@ -0,0 +1,8 @@
import { getGameDb } from '@/lib/db/client'
import { getCrewState } from '@/lib/game/save'
// The ship's crew roster: starting crew plus members found while exploring. Auto-initializes on first hit.
export async function GET() {
const db = await getGameDb()
return Response.json(await getCrewState(db))
}
+14
View File
@@ -5,6 +5,7 @@ import { openApiSpec } from './openapi';
describe('OpenAPI spec', () => {
it('documents every public endpoint', () => {
expect(Object.keys(openApiSpec.paths).sort()).toEqual([
'/api/crew',
'/api/explore',
'/api/jump',
'/api/system',
@@ -13,6 +14,19 @@ describe('OpenAPI spec', () => {
]);
});
it('describes /api/crew as an array of CrewMember and crewFound on ExploreResult', () => {
const crew = openApiSpec.paths['/api/crew'].get.responses[200].content['application/json'].schema;
expect(crew.type).toBe('array');
expect((crew.items as { $ref: string }).$ref).toBe('#/components/schemas/CrewMember');
const member = openApiSpec.components.schemas.CrewMember.properties;
expect(Object.keys(member)).toEqual(['id', 'name', 'role', 'trait_good', 'trait_bad', 'origin']);
const exploreResult = openApiSpec.components.schemas.ExploreResult as { allOf: unknown[] };
const extra = (exploreResult.allOf[1] as { properties: Record<string, unknown> }).properties;
expect(Object.keys(extra)).toEqual(['outcome', 'crewFound']);
});
it('describes the system endpoints as SystemState in and out', () => {
const system = openApiSpec.paths['/api/system'].get.responses[200].content['application/json'].schema;
expect(system.$ref).toBe('#/components/schemas/SystemState');
+35 -1
View File
@@ -168,6 +168,24 @@ export const openApiSpec = {
},
},
},
'/api/crew': {
get: {
summary: 'Crew roster',
description:
'The ship crew in join order — the starting crew (seeded on new game) plus members found while exploring planets/moons. Each member has one good and one bad trait and an origin. Auto-initializes a new game when no save exists yet.',
operationId: 'getCrew',
responses: {
200: {
description: 'Crew roster',
content: {
'application/json': {
schema: { type: 'array', items: { $ref: '#/components/schemas/CrewMember' } },
},
},
},
},
},
},
},
components: {
schemas: {
@@ -245,6 +263,18 @@ export const openApiSpec = {
},
required: ['kind', 'name', 'qty'],
},
CrewMember: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
role: { type: 'string', description: 'Ship role, e.g. PILOT or ENGINEER' },
trait_good: { type: 'string', description: 'The member good trait' },
trait_bad: { type: 'string', description: 'The member bad trait' },
origin: { type: 'string', description: "'STARTING CREW' or '<planet>, <system>' for members found while exploring" },
},
required: ['id', 'name', 'role', 'trait_good', 'trait_bad', 'origin'],
},
SystemState: {
type: 'object',
properties: {
@@ -278,8 +308,12 @@ export const openApiSpec = {
description: 'What this scan found — may be empty (some scans find nothing)',
items: { $ref: '#/components/schemas/FoundItem' },
},
crewFound: {
description: 'A crew member who joined the ship on this scan (~18% of scans), null when none',
anyOf: [{ $ref: '#/components/schemas/CrewMember' }, { type: 'null' }],
},
},
required: ['outcome'],
required: ['outcome', 'crewFound'],
},
],
},