Both stores carried their own ~28-line request<T>, and formatBytes existed twice – the second copy had lost the "B" branch, so a small sprite would have read as 0 KB had its caller ever passed one under a kilobyte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
64 lines
2.0 KiB
TypeScript
64 lines
2.0 KiB
TypeScript
import type { Project } from '../model/Project';
|
|
import { apiRequest } from './http';
|
|
import {
|
|
type LoadedProject,
|
|
type ProjectStore,
|
|
type ProjectSummary,
|
|
StaleRevisionError,
|
|
} from './ProjectStore';
|
|
import { deserializeProject, serializeProject } from './serialize';
|
|
|
|
export class HttpProjectStore implements ProjectStore {
|
|
constructor(private readonly endpoint: string) {}
|
|
|
|
async list(): Promise<ProjectSummary[]> {
|
|
return this.request<ProjectSummary[]>('GET', 'projects');
|
|
}
|
|
|
|
async load(id: string): Promise<LoadedProject> {
|
|
const body = await this.request<{ revision: number; project: unknown }>(
|
|
'GET',
|
|
`projects/${encodeURIComponent(id)}`,
|
|
);
|
|
return { project: deserializeProject(body.project, id), revision: body.revision };
|
|
}
|
|
|
|
async save(project: Project, revision: number): Promise<number> {
|
|
const body = await this.request<{ revision: number }>(
|
|
'PUT',
|
|
`projects/${encodeURIComponent(project.id)}`,
|
|
{
|
|
revision,
|
|
project: serializeProject(project),
|
|
},
|
|
);
|
|
return body.revision;
|
|
}
|
|
|
|
async create(name: string): Promise<LoadedProject> {
|
|
const body = await this.request<{ revision: number; project: unknown }>('POST', 'projects', { name });
|
|
const project = deserializeProject(body.project);
|
|
return { project, revision: body.revision };
|
|
}
|
|
|
|
async delete(id: string): Promise<void> {
|
|
await this.request<void>('DELETE', `projects/${encodeURIComponent(id)}`);
|
|
}
|
|
|
|
private async request<T>(method: string, route: string, payload?: unknown): Promise<T> {
|
|
return apiRequest<T>(
|
|
this.endpoint,
|
|
method,
|
|
route,
|
|
{
|
|
headers: payload !== undefined ? { 'Content-Type': 'application/json' } : {},
|
|
body: payload !== undefined ? JSON.stringify(payload) : undefined,
|
|
},
|
|
(status, data) =>
|
|
status === 409
|
|
? new StaleRevisionError((data as { revision?: number } | null)?.revision ?? -1)
|
|
: undefined,
|
|
);
|
|
}
|
|
}
|