initial commit
This commit is contained in:
18
packages/contracts/package.json
Normal file
18
packages/contracts/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@hexahost/contracts",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hexahost/typescript-config": "workspace:*",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
}
|
||||
32
packages/contracts/src/error.ts
Normal file
32
packages/contracts/src/error.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const problemDetailsSchema = z.object({
|
||||
type: z.string().url().or(z.literal('about:blank')),
|
||||
title: z.string(),
|
||||
status: z.number().int(),
|
||||
detail: z.string().optional(),
|
||||
instance: z.string().optional(),
|
||||
errors: z
|
||||
.array(
|
||||
z.object({
|
||||
field: z.string().optional(),
|
||||
message: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type ProblemDetails = z.infer<typeof problemDetailsSchema>;
|
||||
|
||||
export function createProblemDetails(
|
||||
input: Omit<ProblemDetails, 'type'> & { type?: string },
|
||||
): ProblemDetails {
|
||||
return problemDetailsSchema.parse({
|
||||
type: input.type ?? 'about:blank',
|
||||
title: input.title,
|
||||
status: input.status,
|
||||
detail: input.detail,
|
||||
instance: input.instance,
|
||||
errors: input.errors,
|
||||
});
|
||||
}
|
||||
22
packages/contracts/src/health.ts
Normal file
22
packages/contracts/src/health.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const healthStatusSchema = z.enum(['ok', 'degraded', 'error']);
|
||||
|
||||
export const healthCheckSchema = z.object({
|
||||
name: z.string(),
|
||||
status: healthStatusSchema,
|
||||
message: z.string().optional(),
|
||||
durationMs: z.number().nonnegative().optional(),
|
||||
});
|
||||
|
||||
export const healthResponseSchema = z.object({
|
||||
status: healthStatusSchema,
|
||||
service: z.string(),
|
||||
version: z.string().optional(),
|
||||
timestamp: z.string().datetime(),
|
||||
checks: z.array(healthCheckSchema).optional(),
|
||||
});
|
||||
|
||||
export type HealthStatus = z.infer<typeof healthStatusSchema>;
|
||||
export type HealthCheck = z.infer<typeof healthCheckSchema>;
|
||||
export type HealthResponse = z.infer<typeof healthResponseSchema>;
|
||||
23
packages/contracts/src/index.ts
Normal file
23
packages/contracts/src/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export {
|
||||
healthStatusSchema,
|
||||
healthCheckSchema,
|
||||
healthResponseSchema,
|
||||
type HealthStatus,
|
||||
type HealthCheck,
|
||||
type HealthResponse,
|
||||
} from './health';
|
||||
|
||||
export {
|
||||
problemDetailsSchema,
|
||||
createProblemDetails,
|
||||
type ProblemDetails,
|
||||
} from './error';
|
||||
|
||||
export {
|
||||
paginationQuerySchema,
|
||||
paginationMetaSchema,
|
||||
createPaginatedResponseSchema,
|
||||
buildPaginationMeta,
|
||||
type PaginationQuery,
|
||||
type PaginationMeta,
|
||||
} from './pagination';
|
||||
43
packages/contracts/src/pagination.ts
Normal file
43
packages/contracts/src/pagination.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const paginationQuerySchema = z.object({
|
||||
page: z.coerce.number().int().min(1).default(1),
|
||||
pageSize: z.coerce.number().int().min(1).max(100).default(20),
|
||||
sortBy: z.string().optional(),
|
||||
sortOrder: z.enum(['asc', 'desc']).default('desc'),
|
||||
});
|
||||
|
||||
export const paginationMetaSchema = z.object({
|
||||
page: z.number().int().min(1),
|
||||
pageSize: z.number().int().min(1),
|
||||
totalItems: z.number().int().nonnegative(),
|
||||
totalPages: z.number().int().nonnegative(),
|
||||
hasNextPage: z.boolean(),
|
||||
hasPreviousPage: z.boolean(),
|
||||
});
|
||||
|
||||
export function createPaginatedResponseSchema<T extends z.ZodTypeAny>(itemSchema: T) {
|
||||
return z.object({
|
||||
data: z.array(itemSchema),
|
||||
meta: paginationMetaSchema,
|
||||
});
|
||||
}
|
||||
|
||||
export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
|
||||
export type PaginationMeta = z.infer<typeof paginationMetaSchema>;
|
||||
|
||||
export function buildPaginationMeta(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
totalItems: number,
|
||||
): PaginationMeta {
|
||||
const totalPages = totalItems === 0 ? 0 : Math.ceil(totalItems / pageSize);
|
||||
return paginationMetaSchema.parse({
|
||||
page,
|
||||
pageSize,
|
||||
totalItems,
|
||||
totalPages,
|
||||
hasNextPage: page < totalPages,
|
||||
hasPreviousPage: page > 1,
|
||||
});
|
||||
}
|
||||
9
packages/contracts/tsconfig.json
Normal file
9
packages/contracts/tsconfig.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@hexahost/typescript-config/library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user