chore: add formatting

This commit is contained in:
2026-06-01 21:34:05 +00:00
parent f64d5a73d2
commit 6cc028305a
21 changed files with 542 additions and 177 deletions
+25 -8
View File
@@ -52,7 +52,9 @@ describe("api", () => {
const { api, cleanup } = withApi();
const response = api.getDrawing(
Object.assign(new Request("http://local/api/drawings/missing"), { params: { id: "missing" } }),
Object.assign(new Request("http://local/api/drawings/missing"), {
params: { id: "missing" },
}),
);
expect(response.status).toBe(404);
@@ -199,9 +201,13 @@ describe("api", () => {
},
});
const stored = await api.getDrawing(
Object.assign(new Request(`http://local/api/drawings/${created.id}`), { params: { id: created.id } }),
).json();
const stored = await api
.getDrawing(
Object.assign(new Request(`http://local/api/drawings/${created.id}`), {
params: { id: created.id },
}),
)
.json();
expect(stored.elements).toEqual([{ id: "server" }]);
cleanup();
});
@@ -211,7 +217,10 @@ describe("api", () => {
const created = await api.createDrawing().json();
const disabled = await api.getDrawingPublication(
Object.assign(new Request(`http://local/api/drawings/${created.id}/publication`), { params: { id: created.id } }),
Object.assign(
new Request(`http://local/api/drawings/${created.id}/publication`),
{ params: { id: created.id } },
),
);
expect(disabled.status).toBe(200);
expect(await disabled.json()).toEqual({ enabled: false });
@@ -236,7 +245,10 @@ describe("api", () => {
});
const enabled = await api.getDrawingPublication(
Object.assign(new Request(`http://local/api/drawings/${created.id}/publication`), { params: { id: created.id } }),
Object.assign(
new Request(`http://local/api/drawings/${created.id}/publication`),
{ params: { id: created.id } },
),
);
expect(await enabled.json()).toEqual({
enabled: true,
@@ -394,7 +406,10 @@ describe("api", () => {
);
const enabled = await api.getPublicDrawing(
Object.assign(new Request("http://local/api/public/drawings/public-flow"), { params: { slug: "public-flow" } }),
Object.assign(
new Request("http://local/api/public/drawings/public-flow"),
{ params: { slug: "public-flow" } },
),
);
expect(enabled.status).toBe(200);
@@ -406,7 +421,9 @@ describe("api", () => {
});
const missing = await api.getPublicDrawing(
Object.assign(new Request("http://local/api/public/drawings/missing"), { params: { slug: "missing" } }),
Object.assign(new Request("http://local/api/public/drawings/missing"), {
params: { slug: "missing" },
}),
);
expect(missing.status).toBe(404);
cleanup();
+48 -12
View File
@@ -1,7 +1,12 @@
import { type DrawingStore } from "./db";
import { normalizePublicationSlug } from "../core/publication";
import { type DrawingMeta, type DrawingPublication } from "../core/shared";
import { HttpError, normalizeTitle, parseJsonBody, parseSceneText } from "../core/scene";
import {
HttpError,
normalizeTitle,
parseJsonBody,
parseSceneText,
} from "../core/scene";
type RouteRequest = Request & {
params?: Record<string, string>;
@@ -34,7 +39,9 @@ function toMeta(drawing: DrawingMeta): DrawingMeta {
};
}
function parseExpectedRevision(raw: Record<string, unknown>): number | undefined {
function parseExpectedRevision(
raw: Record<string, unknown>,
): number | undefined {
if (!Object.hasOwn(raw, "expectedRevision")) {
return undefined;
}
@@ -78,7 +85,10 @@ export function createApi(store: DrawingStore) {
const drawing = id ? store.getDrawing(id) : null;
if (!drawing) {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
return json({
@@ -99,7 +109,10 @@ export function createApi(store: DrawingStore) {
const drawing = store.getPublicDrawing(slug);
if (!drawing) {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
return json(drawing);
@@ -136,7 +149,10 @@ export function createApi(store: DrawingStore) {
});
if (!result.ok && result.reason === "not_found") {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
if (!result.ok && result.reason === "conflict") {
@@ -163,7 +179,10 @@ export function createApi(store: DrawingStore) {
deleteDrawing(request: RouteRequest) {
const id = request.params?.id;
if (!id) {
return json({ ok: false, error: "Missing drawing id" }, { status: 400 });
return json(
{ ok: false, error: "Missing drawing id" },
{ status: 400 },
);
}
const { deleted, next } = store.deleteDrawing(id);
@@ -183,7 +202,10 @@ export function createApi(store: DrawingStore) {
const publication = store.getDrawingPublication(id);
if (!publication) {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
return jsonPublication(publication);
@@ -199,11 +221,16 @@ export function createApi(store: DrawingStore) {
throw new HttpError(400, "Missing drawing id");
}
const raw = parseJsonBody<Record<string, unknown>>(await request.text());
const raw = parseJsonBody<Record<string, unknown>>(
await request.text(),
);
if (raw.enabled === false) {
const result = store.disableDrawingPublication(id);
if (!result.ok) {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
return jsonPublication(result.publication);
@@ -215,7 +242,10 @@ export function createApi(store: DrawingStore) {
const drawing = store.getDrawing(id);
if (!drawing) {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
const result = store.publishDrawing(id, {
@@ -223,11 +253,17 @@ export function createApi(store: DrawingStore) {
});
if (!result.ok && result.reason === "not_found") {
return json({ ok: false, error: "Drawing not found" }, { status: 404 });
return json(
{ ok: false, error: "Drawing not found" },
{ status: 404 },
);
}
if (!result.ok && result.reason === "slug_conflict") {
return json({ ok: false, error: "Published slug already exists" }, { status: 409 });
return json(
{ ok: false, error: "Published slug already exists" },
{ status: 409 },
);
}
return jsonPublication(result.publication);
+23 -6
View File
@@ -79,7 +79,9 @@ describe("drawing store", () => {
expect(stale.ok).toBe(false);
expect(stale.ok ? null : stale.reason).toBe("conflict");
expect(store.getDrawing(created.id)?.revision).toBe(1);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([{ id: "first" }]);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([
{ id: "first" },
]);
});
test("keeps the revision unchanged when the scene is already stored", () => {
@@ -109,13 +111,21 @@ describe("drawing store", () => {
files: {},
};
const first = store.updateDrawing(created.id, { expectedRevision: 0, scene });
const first = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(first.ok ? first.drawing.revision : null).toBe(1);
const repeated = store.updateDrawing(created.id, { expectedRevision: 0, scene });
const repeated = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(repeated.ok).toBe(true);
expect(repeated.ok ? repeated.drawing.revision : null).toBe(1);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([{ id: "same" }]);
expect(store.getDrawing(created.id)?.scene.elements).toEqual([
{ id: "same" },
]);
});
test("increments the revision when the title changes but the scene does not", () => {
@@ -127,10 +137,17 @@ describe("drawing store", () => {
files: {},
};
const first = store.updateDrawing(created.id, { expectedRevision: 0, scene });
const first = store.updateDrawing(created.id, {
expectedRevision: 0,
scene,
});
expect(first.ok ? first.drawing.revision : null).toBe(1);
const renamed = store.updateDrawing(created.id, { expectedRevision: 1, title: "Renamed", scene });
const renamed = store.updateDrawing(created.id, {
expectedRevision: 1,
title: "Renamed",
scene,
});
expect(renamed.ok).toBe(true);
expect(renamed.ok ? renamed.drawing.title : null).toBe("Renamed");
expect(renamed.ok ? renamed.drawing.revision : null).toBe(2);
+73 -20
View File
@@ -2,7 +2,13 @@ import { Database } from "bun:sqlite";
import { randomUUID } from "node:crypto";
import { mkdirSync } from "node:fs";
import { dirname } from "node:path";
import { DEFAULT_TITLE, type DrawingMeta, type DrawingPublication, type DrawingRecord, type ScenePayload } from "../core/shared";
import {
DEFAULT_TITLE,
type DrawingMeta,
type DrawingPublication,
type DrawingRecord,
type ScenePayload,
} from "../core/shared";
import { emptyScene, normalizeScene, normalizeTitle } from "../core/scene";
type DrawingRow = {
@@ -69,7 +75,9 @@ function readMeta(row: DrawingMetaRow | null | undefined): DrawingMeta | null {
};
}
function readPublication(row: DrawingPublicationRow | null | undefined): DrawingPublication | null {
function readPublication(
row: DrawingPublicationRow | null | undefined,
): DrawingPublication | null {
if (!row) {
return null;
}
@@ -93,7 +101,8 @@ function readPublicDrawing(row: PublicDrawingRow | null | undefined) {
try {
scene = normalizeScene(JSON.parse(row.data));
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown scene parsing error";
const message =
error instanceof Error ? error.message : "Unknown scene parsing error";
throw new Error(`Invalid stored public scene: ${message}`);
}
@@ -112,7 +121,8 @@ function readRow(row: DrawingRow | null | undefined): DrawingRecord | null {
try {
scene = normalizeScene(JSON.parse(row.data));
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown scene parsing error";
const message =
error instanceof Error ? error.message : "Unknown scene parsing error";
throw new Error(`Invalid stored scene for drawing ${row.id}: ${message}`);
}
@@ -123,13 +133,15 @@ function readRow(row: DrawingRow | null | undefined): DrawingRecord | null {
}
function ensurePublicationColumns(db: Database) {
const columns = db
.query("PRAGMA table_info(drawings)")
.all() as Array<{ name: string }>;
const columns = db.query("PRAGMA table_info(drawings)").all() as Array<{
name: string;
}>;
const names = new Set(columns.map((column) => column.name));
if (!names.has("public_enabled")) {
db.exec("ALTER TABLE drawings ADD COLUMN public_enabled INTEGER NOT NULL DEFAULT 0");
db.exec(
"ALTER TABLE drawings ADD COLUMN public_enabled INTEGER NOT NULL DEFAULT 0",
);
}
if (!names.has("public_slug")) {
@@ -138,7 +150,10 @@ function ensurePublicationColumns(db: Database) {
}
function isSlugConflictError(error: unknown): boolean {
return error instanceof Error && error.message.includes("UNIQUE constraint failed: drawings.public_slug");
return (
error instanceof Error &&
error.message.includes("UNIQUE constraint failed: drawings.public_slug")
);
}
export function createDrawingStore(databasePath = resolveDatabasePath()) {
@@ -310,8 +325,12 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
return { ok: true, drawing: existing };
}
const nextTitle = hasTitle ? normalizeTitle(update.title) : existingRow.title;
const nextScene = hasScene ? JSON.stringify(update.scene) : existingRow.data;
const nextTitle = hasTitle
? normalizeTitle(update.title)
: existingRow.title;
const nextScene = hasScene
? JSON.stringify(update.scene)
: existingRow.data;
const titleChanged = nextTitle !== existingRow.title;
const sceneChanged = nextScene !== existingRow.data;
@@ -319,7 +338,10 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
return { ok: true, drawing: existing };
}
if (update.expectedRevision !== undefined && update.expectedRevision !== existingRow.revision) {
if (
update.expectedRevision !== undefined &&
update.expectedRevision !== existingRow.revision
) {
return { ok: false, reason: "conflict", drawing: existing };
}
@@ -328,17 +350,36 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
changes =
update.expectedRevision === undefined
? Number(updateBothQuery.run(id, nextTitle, nextScene).changes)
: Number(updateBothExpectedQuery.run(id, nextTitle, nextScene, update.expectedRevision).changes);
: Number(
updateBothExpectedQuery.run(
id,
nextTitle,
nextScene,
update.expectedRevision,
).changes,
);
} else if (sceneChanged) {
changes =
update.expectedRevision === undefined
? Number(updateSceneQuery.run(id, nextScene).changes)
: Number(updateSceneExpectedQuery.run(id, nextScene, update.expectedRevision).changes);
: Number(
updateSceneExpectedQuery.run(
id,
nextScene,
update.expectedRevision,
).changes,
);
} else {
changes =
update.expectedRevision === undefined
? Number(updateTitleQuery.run(id, nextTitle).changes)
: Number(updateTitleExpectedQuery.run(id, nextTitle, update.expectedRevision).changes);
: Number(
updateTitleExpectedQuery.run(
id,
nextTitle,
update.expectedRevision,
).changes,
);
}
const drawing = getDrawing(id);
@@ -353,7 +394,10 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
return { ok: false, reason: "conflict", drawing };
}
function deleteDrawing(id: string): { deleted: boolean; next: DrawingMeta | null } {
function deleteDrawing(id: string): {
deleted: boolean;
next: DrawingMeta | null;
} {
const changes = Number(deleteQuery.run(id).changes);
if (changes === 0) {
return { deleted: false, next: null };
@@ -366,10 +410,15 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
}
function getDrawingPublication(id: string): DrawingPublication | null {
return readPublication(getPublicationQuery.get(id) as DrawingPublicationRow | null | undefined);
return readPublication(
getPublicationQuery.get(id) as DrawingPublicationRow | null | undefined,
);
}
function publishDrawing(id: string, publication: { slug: string }): DrawingPublicationUpdateResult {
function publishDrawing(
id: string,
publication: { slug: string },
): DrawingPublicationUpdateResult {
try {
const changes = Number(publishQuery.run(id, publication.slug).changes);
if (changes === 0) {
@@ -389,7 +438,9 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
};
}
function disableDrawingPublication(id: string): DrawingPublicationUpdateResult {
function disableDrawingPublication(
id: string,
): DrawingPublicationUpdateResult {
const changes = Number(disablePublicationQuery.run(id).changes);
if (changes === 0) {
return { ok: false, reason: "not_found" };
@@ -402,7 +453,9 @@ export function createDrawingStore(databasePath = resolveDatabasePath()) {
}
function getPublicDrawing(slug: string) {
return readPublicDrawing(getPublicDrawingQuery.get(slug) as PublicDrawingRow | null | undefined);
return readPublicDrawing(
getPublicDrawingQuery.get(slug) as PublicDrawingRow | null | undefined,
);
}
function close() {
+18 -7
View File
@@ -38,12 +38,16 @@ describe("createServer", () => {
test("redirects / to the latest drawing", () => {
const { server, store } = createServerFixture();
const response = server.routes["/"]?.(new Request("http://local/")) as Response;
const response = server.routes["/"]?.(
new Request("http://local/"),
) as Response;
const created = store.listDrawings();
expect(created).toHaveLength(1);
expect(response.status).toBe(302);
expect(response.headers.get("location")).toBe(`http://local/d/${created[0]?.id}`);
expect(response.headers.get("location")).toBe(
`http://local/d/${created[0]?.id}`,
);
});
test("keeps Bun responsible for API routes", async () => {
@@ -67,9 +71,12 @@ describe("createServer", () => {
store.publishDrawing(drawing.id, { slug: "published-note" });
const response = await server.routes["/api/public/drawings/:slug"]?.GET?.(
Object.assign(new Request("http://local/api/public/drawings/published-note"), {
params: { slug: "published-note" },
}),
Object.assign(
new Request("http://local/api/public/drawings/published-note"),
{
params: { slug: "published-note" },
},
),
);
expect(response?.status).toBe(200);
@@ -85,7 +92,9 @@ describe("createServer", () => {
const { server } = createServerFixture();
const response = await server.routes["/api/public/drawings/:slug"]?.GET?.(
Object.assign(new Request("http://local/api/public/drawings/missing"), { params: { slug: "missing" } }),
Object.assign(new Request("http://local/api/public/drawings/missing"), {
params: { slug: "missing" },
}),
);
expect(response?.status).toBe(404);
@@ -93,7 +102,9 @@ describe("createServer", () => {
test("returns JSON 404 for unmatched requests", async () => {
const { server } = createServerFixture();
const response = await server.fetch(new Request("http://local/index-abc123.js"));
const response = await server.fetch(
new Request("http://local/index-abc123.js"),
);
expect(response.status).toBe(404);
expect(await response.json()).toEqual({ ok: false, error: "Not found" });
+14 -7
View File
@@ -25,19 +25,26 @@ export function createServer(options: CreateServerOptions = {}) {
POST: () => api.createDrawing(),
},
"/api/drawings/:id": {
GET: (request: Request & { params: Record<string, string> }) => api.getDrawing(request),
PUT: (request: Request & { params: Record<string, string> }) => api.updateDrawing(request),
DELETE: (request: Request & { params: Record<string, string> }) => api.deleteDrawing(request),
GET: (request: Request & { params: Record<string, string> }) =>
api.getDrawing(request),
PUT: (request: Request & { params: Record<string, string> }) =>
api.updateDrawing(request),
DELETE: (request: Request & { params: Record<string, string> }) =>
api.deleteDrawing(request),
},
"/api/public/drawings/:slug": {
GET: (request: Request & { params: Record<string, string> }) => api.getPublicDrawing(request),
GET: (request: Request & { params: Record<string, string> }) =>
api.getPublicDrawing(request),
},
"/api/drawings/:id/publication": {
GET: (request: Request & { params: Record<string, string> }) => api.getDrawingPublication(request),
PUT: (request: Request & { params: Record<string, string> }) => api.updateDrawingPublication(request),
GET: (request: Request & { params: Record<string, string> }) =>
api.getDrawingPublication(request),
PUT: (request: Request & { params: Record<string, string> }) =>
api.updateDrawingPublication(request),
},
},
fetch: (_request: Request) => Response.json({ ok: false, error: "Not found" }, { status: 404 }),
fetch: (_request: Request) =>
Response.json({ ok: false, error: "Not found" }, { status: 404 }),
} satisfies Parameters<typeof Bun.serve>[0];
}