Track and save on groceries

refactor(api): add model title helper

Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>

+20 -8
+3 -2
packages/api/src/schema/pagination.ts
··· 1 1 import z from "zod/v4"; 2 + import { schemaTitle } from "../utils.js"; 2 3 3 4 export const DEFAULT_LIMIT = 50; 4 5 export const Total = z.coerce.number().int().min(0); ··· 11 12 cursor: cursor.optional(), 12 13 }) 13 14 .meta({ 14 - title: `${cursor.meta()?.title ?? "UNKNOWN_OBJECT"}PageOptions`, 15 + title: `${schemaTitle(cursor)}PageOptions`, 15 16 }); 16 17 17 18 export const paginated = <T extends z.ZodTypeAny>(item: T) => ··· 22 23 items: z.array(item), 23 24 }) 24 25 .meta({ 25 - title: `${item.meta()?.title ?? "UNKNOWN_OBJECT"}s`, 26 + title: `${schemaTitle(item)}s`, 26 27 });
+2 -6
packages/api/src/spec.ts
··· 6 6 import { Brand, Brands } from "./schema/brands.js"; 7 7 import { ExtendedProduct, Product, Products } from "./schema/products.js"; 8 8 import z from "zod/v4"; 9 - import assert from "node:assert"; 9 + import { schemaTitle } from "./utils.js"; 10 10 11 11 const generator = new OpenAPIGenerator({ 12 12 schemaConverters: [new ZodToJsonSchemaConverter()], ··· 25 25 > = {}; 26 26 27 27 for (const [i, schema] of schemas.entries()) { 28 - const meta = schema.meta(); 29 - assert(meta, `Schema ${i} has meta`); 30 - assert(meta.title, `Schema ${i} has title in meta`); 31 - 32 - commonSchemas[meta.title] = { 28 + commonSchemas[schemaTitle(schema)] = { 33 29 strategy, 34 30 schema, 35 31 };
+15
packages/api/src/utils.ts
··· 1 + import z from "zod/v4"; 2 + 3 + export class SchemaNoTitleError extends Error { 4 + constructor(schema: z.ZodType) { 5 + super(`Schema has no title metadata ${schema}`) 6 + } 7 + } 8 + 9 + export const schemaTitle = (schema: z.ZodType) => { 10 + const meta = schema.meta(); 11 + if (!meta?.title) { 12 + throw new SchemaNoTitleError(schema); 13 + } 14 + return meta.title; 15 + };