Initial commit

This commit is contained in:
fraqtal
2026-07-12 08:15:46 +00:00
commit ee0fec0691
1397 changed files with 127242 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
export class InputParseError extends Error {
constructor(message: string, options?: ErrorOptions) {
super(message, options);
this.name = "InputParseError";
}
}

View File

@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";
import { PageNotFoundError } from "./page";
import { InputParseError } from "./common";
describe("PageNotFoundError", () => {
it("uses the default message when none is given", () => {
const err = new PageNotFoundError();
expect(err).toBeInstanceOf(Error);
expect(err.message).toBe("Page not found");
});
it("uses a custom message when provided", () => {
const err = new PageNotFoundError("could not find page /about");
expect(err.message).toBe("could not find page /about");
});
});
describe("InputParseError", () => {
it("is an instance of Error with the given message", () => {
const err = new InputParseError("invalid input");
expect(err).toBeInstanceOf(Error);
expect(err.message).toBe("invalid input");
});
});

View File

@@ -0,0 +1,6 @@
export class PageNotFoundError extends Error {
constructor(message = "Page not found", options?: ErrorOptions) {
super(message, options);
this.name = "PageNotFoundError";
}
}

View File

@@ -0,0 +1,58 @@
import { describe, expect, it } from "vitest";
import { pageSchema, pageStatusSchema } from "./page";
describe("pageSchema", () => {
it("accepts a minimal valid page", () => {
const result = pageSchema.parse({
id: "p1",
title: "About",
slug: "about",
hero: { heading: "About us" },
layout: [],
seo: { title: "About — My App" },
createdAt: new Date(),
updatedAt: new Date(),
});
expect(result.status).toBe("draft");
expect(result.publishedAt).toBeNull();
});
it("accepts a published page with publishedAt", () => {
const result = pageSchema.parse({
id: "p1",
title: "About",
slug: "about",
hero: { heading: "About us" },
layout: [],
status: "published",
publishedAt: new Date(),
seo: { title: "About" },
createdAt: new Date(),
updatedAt: new Date(),
});
expect(result.status).toBe("published");
expect(result.publishedAt).toBeInstanceOf(Date);
});
it("rejects empty title", () => {
expect(() =>
pageSchema.parse({
id: "p1",
title: "",
slug: "about",
hero: { heading: "h" },
layout: [],
seo: { title: "x" },
createdAt: new Date(),
updatedAt: new Date(),
}),
).toThrow();
});
});
describe("pageStatusSchema", () => {
it("accepts draft and published", () => {
expect(pageStatusSchema.parse("draft")).toBe("draft");
expect(pageStatusSchema.parse("published")).toBe("published");
});
});

View File

@@ -0,0 +1,29 @@
import { z } from "zod";
export const pageStatusSchema = z.enum(["draft", "published"]);
export const heroSchema = z.object({
heading: z.string().min(1).max(255),
subheading: z.string().optional(),
imageId: z.string().optional(),
});
export const pageSchema = z.object({
id: z.string(),
title: z.string().min(1).max(255),
slug: z.string().min(1).max(255),
hero: heroSchema,
layout: z.array(z.unknown()),
status: pageStatusSchema.default("draft"),
publishedAt: z.date().nullable().default(null),
seo: z.object({
title: z.string().min(1),
description: z.string().optional(),
}),
createdAt: z.date(),
updatedAt: z.date(),
});
export type Page = z.infer<typeof pageSchema>;
export type PageStatus = z.infer<typeof pageStatusSchema>;
export type Hero = z.infer<typeof heroSchema>;

View File

@@ -0,0 +1,23 @@
import { describe, expect, it } from "vitest";
import { siteSettingsSchema } from "./site-settings";
describe("siteSettingsSchema", () => {
it("accepts a valid site settings object", () => {
const result = siteSettingsSchema.parse({
siteName: "My Site",
siteDescription: "A great site",
});
expect(result.siteName).toBe("My Site");
expect(result.siteDescription).toBe("A great site");
});
it("accepts site settings without a description", () => {
const result = siteSettingsSchema.parse({ siteName: "My Site" });
expect(result.siteName).toBe("My Site");
expect(result.siteDescription).toBeUndefined();
});
it("rejects an empty siteName", () => {
expect(() => siteSettingsSchema.parse({ siteName: "" })).toThrow();
});
});

View File

@@ -0,0 +1,8 @@
import { z } from "zod";
export const siteSettingsSchema = z.object({
siteName: z.string().min(1),
siteDescription: z.string().optional(),
});
export type SiteSettings = z.infer<typeof siteSettingsSchema>;