Files
CharacterGardenStack/charactergarden/app/src/app.ts
spencer 998635f542 feat: implement processTurn function to handle turn processing and world state updates
refactor: remove legacy types.ts file and update frontend to use new contracts

feat: add applyActions function to manage action application and world state mutation

chore: remove empty .gitkeep file from sqlite data directory

refactor: update frontend App component to align with new API contracts and improve UX

docs: revise project.md to reflect updated architecture and system requirements

docs: update thoughts.md with current status, architecture decisions, and remaining checks
2026-04-24 01:04:17 -04:00

128 lines
2.6 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { createDatabase, CharacterGardenDatabase } from "./db";
import type { Entity } from "./contracts/entity";
import type { Turn } from "./contracts/turn";
import type { WorldState } from "./contracts/world";
import { processTurn, ProcessTurnResponse } from "./turns/processTurn";
export interface AppSnapshot {
worldState: WorldState;
turns: Turn[];
}
export interface CharacterGardenApp {
db: CharacterGardenDatabase;
getSnapshot(): AppSnapshot;
processTurn(rawText: string): ProcessTurnResponse;
reset(): AppSnapshot;
}
function createSeedWorldState(): WorldState {
const now = Date.now();
const entities: Record<string, Entity> = {
room_start: {
id: "room_start",
name: "Start Room",
type: "room",
attributes: {
description: "A plain room with a locked door.",
},
},
room_exit: {
id: "room_exit",
name: "Exit Room",
type: "room",
attributes: {
description: "A simple room beyond the door.",
},
},
player: {
id: "player",
name: "Player",
type: "character",
attributes: {
location: "room_start",
has_key_1: false,
},
},
door_1: {
id: "door_1",
name: "Old Door",
type: "door",
attributes: {
location: "room_start",
openable: true,
locked: true,
requiredKey: "key_1",
open: false,
},
},
key_1: {
id: "key_1",
name: "Brass Key",
type: "item",
attributes: {
location: "room_start",
takeable: true,
},
},
};
return {
id: randomUUID(),
entities,
metadata: {
domain: "door_key_mvp",
version: 1,
},
createdAt: now,
};
}
function ensureSeedState(db: CharacterGardenDatabase): WorldState {
db.init();
const latest = db.getLatestWorldState();
if (latest) {
return latest;
}
const seed = createSeedWorldState();
db.upsertEntities(Object.values(seed.entities));
db.insertWorldState(null, seed);
return seed;
}
export function createCharacterGardenApp(dbPath: string): CharacterGardenApp {
const db = createDatabase({ dbPath });
let worldState = ensureSeedState(db);
return {
db,
getSnapshot() {
return {
worldState,
turns: db.listTurns(),
};
},
processTurn(rawText: string) {
const result = processTurn(rawText, worldState, db);
worldState = result.worldState;
return result;
},
reset() {
db.wipe();
worldState = ensureSeedState(db);
return {
worldState,
turns: db.listTurns(),
};
},
};
}