feat: establish auth listing and image vertical slices

Includes MFA-capable authentication, session and CSRF security, password recovery, listing lifecycle and ownership rules, inventory image metadata, local ImageStorage, migrations, tests, and implementation status documentation. Verification: npm test (62 passing) and integration suite (14 passing).
This commit is contained in:
2026-09-08 17:32:03 -04:00
commit c728016dec
128 changed files with 9788 additions and 0 deletions
View File
+34
View File
@@ -0,0 +1,34 @@
import { z } from 'zod';
export const loginSchema = z
.object({
email: z.string().trim().email().max(320),
password: z.string().min(1).max(1024),
mfaCode: z.string().regex(/^\d{6}$/).optional(),
rememberMe: z.boolean().optional(),
})
.strict();
export const sessionIdSchema = z.uuid();
export const requestPasswordResetSchema = z
.object({ email: z.string().trim().email().max(320) })
.strict();
export const resetPasswordSchema = z
.object({
token: z.string().min(32).max(512),
password: z.string().min(12).max(1024),
})
.strict();
export const enrollMfaSchema = z
.object({ name: z.string().trim().min(1).max(100) })
.strict();
export const verifyMfaSchema = z
.object({
methodId: z.uuid(),
code: z.string().regex(/^\d{6}$/),
})
.strict();
+13
View File
@@ -0,0 +1,13 @@
import { z } from 'zod';
export const createInventoryImageSchema = z.object({
storageKey: z.string().trim().min(1).max(500),
originalFilename: z.string().trim().min(1).max(255),
mimeType: z.enum(['image/jpeg', 'image/png', 'image/webp']),
fileSize: z.int().positive(),
width: z.int().positive(),
height: z.int().positive(),
altText: z.string().trim().max(1000).optional(),
sortOrder: z.int().nonnegative().optional(),
isPrimary: z.boolean().optional(),
}).strict();
+9
View File
@@ -0,0 +1,9 @@
import { z } from 'zod';
export const createInventoryItemSchema = z
.object({
productId: z.uuid(),
conditionId: z.uuid(),
notes: z.string().trim().max(10000).optional(),
})
.strict();
+13
View File
@@ -0,0 +1,13 @@
import { z } from 'zod';
export const createListingSchema = z
.object({
inventoryItemId: z.uuid(),
productName: z.string().trim().min(1).max(255),
categoryId: z.uuid(),
conditionId: z.uuid(),
askingPriceMinor: z.int().positive(),
imageCount: z.int().positive(),
notes: z.string().trim().max(10000).optional(),
})
.strict();
+15
View File
@@ -0,0 +1,15 @@
{
"name": "@pokemon-inventory/validation",
"private": true,
"type": "module",
"exports": {
"./auth": "./auth.js",
"./inventory": "./inventory.js",
"./image": "./image.js",
"./listing": "./listing.js",
"./product": "./product.js"
},
"dependencies": {
"zod": "^4.1.8"
}
}
+10
View File
@@ -0,0 +1,10 @@
import { z } from 'zod';
const identifier = z.uuid();
export const createProductSchema = z
.object({
name: z.string().trim().min(1).max(255),
categoryId: identifier,
})
.strict();