refactor(data): merge DTO models into schemas

This commit is contained in:
2026-07-17 12:47:18 +08:00
parent 2796010971
commit eac3d8f0a7
274 changed files with 1466 additions and 1956 deletions
@@ -0,0 +1,42 @@
import { describe, expect, it } from "vitest";
import { Character } from "@/data/schemas/character";
describe("Character", () => {
it("keeps only the four public character fields", () => {
const character = Character.fromJson({
id: "character_elio",
slug: "elio",
displayName: " Elio Silvestri ",
avatarUrl: "https://cdn.example.com/elio.jpg",
enabled: true,
sortOrder: 1,
});
expect(character.toJson()).toEqual({
id: "character_elio",
slug: "elio",
displayName: "Elio Silvestri",
avatarUrl: "https://cdn.example.com/elio.jpg",
});
});
it("rejects unsafe slugs and non-HTTPS avatars", () => {
expect(() =>
Character.from({
id: "character_aria",
slug: "Aria Profile",
displayName: "Aria",
avatarUrl: "https://cdn.example.com/aria.jpg",
}),
).toThrow();
expect(() =>
Character.from({
id: "character_aria",
slug: "aria",
displayName: "Aria",
avatarUrl: "http://cdn.example.com/aria.jpg",
}),
).toThrow();
});
});
+24
View File
@@ -11,3 +11,27 @@ export const CharacterSchema = z.object({
export type CharacterInput = z.input<typeof CharacterSchema>;
export type CharacterData = z.output<typeof CharacterSchema>;
export class Character {
declare readonly id: string;
declare readonly slug: string;
declare readonly displayName: string;
declare readonly avatarUrl: string;
private constructor(input: CharacterInput) {
Object.assign(this, CharacterSchema.parse(input));
Object.freeze(this);
}
static from(input: CharacterInput): Character {
return new Character(input);
}
static fromJson(json: unknown): Character {
return Character.from(json as CharacterInput);
}
toJson(): CharacterData {
return CharacterSchema.parse(this);
}
}
@@ -1,6 +1,6 @@
import { z } from "zod";
import { CharacterSchema } from "./character";
import { Character, CharacterSchema } from "./character";
export const CharactersResponseSchema = z.object({
items: z.array(CharacterSchema).default([]),
@@ -8,3 +8,29 @@ export const CharactersResponseSchema = z.object({
export type CharactersResponseInput = z.input<typeof CharactersResponseSchema>;
export type CharactersResponseData = z.output<typeof CharactersResponseSchema>;
export class CharactersResponse {
declare readonly items: Character[];
private constructor(input: CharactersResponseInput) {
const data = CharactersResponseSchema.parse(input);
Object.assign(this, {
items: data.items.map((item) => Character.from(item)),
});
Object.freeze(this);
}
static from(input: CharactersResponseInput): CharactersResponse {
return new CharactersResponse(input);
}
static fromJson(json: unknown): CharactersResponse {
return CharactersResponse.from(json as CharactersResponseInput);
}
toJson(): CharactersResponseData {
return CharactersResponseSchema.parse({
items: this.items.map((item) => item.toJson()),
});
}
}
+4
View File
@@ -1,2 +1,6 @@
/**
* @file Automatically generated by barrelsby.
*/
export * from "./character";
export * from "./characters_response";