feat(25-01): Zod schemas, upsert service functions, passing tests

- Add upsertGlobalItemSchema and bulkUpsertGlobalItemsSchema to schemas.ts
- Add UpsertGlobalItemInput and BulkUpsertGlobalItemsInput types to types.ts
- Implement upsertGlobalItem with onConflictDoUpdate and tag sync
- Implement bulkUpsertGlobalItems processing array in single transaction
- Fix migration 0003 to only add new columns + unique constraint
- All 21 tests pass including 8 new upsert operation tests
This commit is contained in:
2026-04-10 10:58:36 +02:00
parent 9093a2c8f6
commit c8ebbf8139
5 changed files with 212 additions and 15 deletions

View File

@@ -102,6 +102,25 @@ export const searchGlobalItemsSchema = z.object({
tags: z.string().optional(),
});
// Catalog upsert schemas
export const upsertGlobalItemSchema = z.object({
brand: z.string().min(1, "Brand is required"),
model: z.string().min(1, "Model is required"),
category: z.string().optional(),
weightGrams: z.number().nonnegative().optional(),
priceCents: z.number().int().nonnegative().optional(),
imageUrl: z.string().url().optional().or(z.literal("")),
description: z.string().optional(),
sourceUrl: z.string().url().optional().or(z.literal("")),
imageCredit: z.string().optional(),
imageSourceUrl: z.string().url().optional().or(z.literal("")),
tags: z.array(z.string().min(1).max(100)).max(20).optional(),
});
export const bulkUpsertGlobalItemsSchema = z.object({
items: z.array(upsertGlobalItemSchema).min(1).max(100),
});
// Profile schemas
export const updateProfileSchema = z.object({
displayName: z.string().max(100).optional(),

View File

@@ -11,6 +11,7 @@ import type {
threads,
} from "../db/schema.ts";
import type {
bulkUpsertGlobalItemsSchema,
createCandidateSchema,
createCategorySchema,
createItemSchema,
@@ -27,6 +28,7 @@ import type {
updateProfileSchema,
updateSetupSchema,
updateThreadSchema,
upsertGlobalItemSchema,
} from "./schemas.ts";
// Types inferred from Zod schemas
@@ -50,6 +52,10 @@ export type UpdateClassification = z.infer<typeof updateClassificationSchema>;
// Global item types
export type SearchGlobalItems = z.infer<typeof searchGlobalItemsSchema>;
export type UpdateProfile = z.infer<typeof updateProfileSchema>;
export type UpsertGlobalItemInput = z.infer<typeof upsertGlobalItemSchema>;
export type BulkUpsertGlobalItemsInput = z.infer<
typeof bulkUpsertGlobalItemsSchema
>;
// Types inferred from Drizzle schema
export type Item = typeof items.$inferSelect;