chore: bootstrap monorepo workspace and shared schemas
This commit is contained in:
23
packages/shared/package.json
Normal file
23
packages/shared/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@family-wishlist/shared",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "echo 'shared is source-only'",
|
||||
"lint": "echo 'skip'",
|
||||
"dev": "echo 'shared is source-only'"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
}
|
||||
17
packages/shared/src/auth.schema.ts
Normal file
17
packages/shared/src/auth.schema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const loginSchema = z.object({
|
||||
username: z.string().trim().min(3).max(64),
|
||||
password: z.string().min(8).max(200),
|
||||
});
|
||||
|
||||
export type LoginInput = z.infer<typeof loginSchema>;
|
||||
|
||||
export const authUserSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
slug: z.string(),
|
||||
displayName: z.string(),
|
||||
});
|
||||
|
||||
export type AuthUser = z.infer<typeof authUserSchema>;
|
||||
5
packages/shared/src/index.ts
Normal file
5
packages/shared/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './auth.schema.js';
|
||||
export * from './profile.schema.js';
|
||||
export * from './wish.schema.js';
|
||||
export * from './public.schema.js';
|
||||
export * from './types.js';
|
||||
30
packages/shared/src/profile.schema.ts
Normal file
30
packages/shared/src/profile.schema.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const slugRegex = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])?$/;
|
||||
|
||||
export const profileSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
slug: z.string(),
|
||||
displayName: z.string(),
|
||||
bio: z.string().nullable(),
|
||||
avatarUrl: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
});
|
||||
|
||||
export type Profile = z.infer<typeof profileSchema>;
|
||||
|
||||
export const updateProfileSchema = z.object({
|
||||
slug: z
|
||||
.string()
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.regex(slugRegex, 'Slug must be 3-32 chars, lowercase letters, digits, hyphens')
|
||||
.optional(),
|
||||
displayName: z.string().trim().min(1).max(64).optional(),
|
||||
bio: z.string().trim().max(500).nullable().optional(),
|
||||
avatarUrl: z.string().url().nullable().optional(),
|
||||
});
|
||||
|
||||
export type UpdateProfileInput = z.infer<typeof updateProfileSchema>;
|
||||
16
packages/shared/src/public.schema.ts
Normal file
16
packages/shared/src/public.schema.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const publicProfileSchema = z.object({
|
||||
slug: z.string(),
|
||||
displayName: z.string(),
|
||||
bio: z.string().nullable(),
|
||||
avatarUrl: z.string().nullable(),
|
||||
});
|
||||
|
||||
export type PublicProfile = z.infer<typeof publicProfileSchema>;
|
||||
|
||||
export const markSeenSchema = z.object({
|
||||
wishIds: z.array(z.string().cuid2().or(z.string())).min(1).max(500),
|
||||
});
|
||||
|
||||
export type MarkSeenInput = z.infer<typeof markSeenSchema>;
|
||||
9
packages/shared/src/types.ts
Normal file
9
packages/shared/src/types.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface ApiError {
|
||||
error: string;
|
||||
message: string;
|
||||
details?: unknown;
|
||||
}
|
||||
|
||||
export interface VersionInfo {
|
||||
backend: string;
|
||||
}
|
||||
61
packages/shared/src/wish.schema.ts
Normal file
61
packages/shared/src/wish.schema.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const wishStatus = z.enum(['ACTIVE', 'ARCHIVED', 'COMPLETED', 'DELETED']);
|
||||
export type WishStatus = z.infer<typeof wishStatus>;
|
||||
|
||||
export const imageSource = z.enum(['DEFAULT', 'OG', 'UPLOADED']);
|
||||
export type ImageSource = z.infer<typeof imageSource>;
|
||||
|
||||
export const wishSchema = z.object({
|
||||
id: z.string(),
|
||||
title: z.string(),
|
||||
price: z.string().nullable(),
|
||||
currency: z.string(),
|
||||
url: z.string().nullable(),
|
||||
comment: z.string().nullable(),
|
||||
imageUrl: z.string().nullable(),
|
||||
imageSource: imageSource,
|
||||
status: wishStatus,
|
||||
createdAt: z.string(),
|
||||
updatedAt: z.string(),
|
||||
archivedAt: z.string().nullable(),
|
||||
completedAt: z.string().nullable(),
|
||||
deletedAt: z.string().nullable(),
|
||||
sourceWishId: z.string().nullable(),
|
||||
isNewForGuest: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type Wish = z.infer<typeof wishSchema>;
|
||||
|
||||
export const createWishSchema = z.object({
|
||||
title: z.string().trim().min(1).max(200),
|
||||
price: z
|
||||
.union([
|
||||
z.string().trim().regex(/^\d+(\.\d{1,2})?$/, 'Price must be a number with up to 2 decimals'),
|
||||
z.literal(''),
|
||||
z.null(),
|
||||
])
|
||||
.optional()
|
||||
.transform((v) => (v === '' || v == null ? null : v)),
|
||||
currency: z.string().trim().length(3).toUpperCase().default('RUB'),
|
||||
url: z
|
||||
.union([z.string().trim().url(), z.literal(''), z.null()])
|
||||
.optional()
|
||||
.transform((v) => (v === '' || v == null ? null : v)),
|
||||
comment: z
|
||||
.union([z.string().trim().max(2000), z.literal(''), z.null()])
|
||||
.optional()
|
||||
.transform((v) => (v === '' || v == null ? null : v)),
|
||||
});
|
||||
|
||||
export type CreateWishInput = z.infer<typeof createWishSchema>;
|
||||
|
||||
export const updateWishSchema = createWishSchema.partial();
|
||||
export type UpdateWishInput = z.infer<typeof updateWishSchema>;
|
||||
|
||||
export const wishStatusQuery = z
|
||||
.enum(['active', 'archived', 'completed', 'deleted'])
|
||||
.default('active');
|
||||
|
||||
export const NEW_BADGE_DAYS = 5;
|
||||
export const TRASH_RETENTION_DAYS = 30;
|
||||
9
packages/shared/tsconfig.json
Normal file
9
packages/shared/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user