import { Static, TSchema, Type } from "@sinclair/typebox"; export class ResponseError { status: T; data: { error: string; details?: P; } constructor(status: T, message: string, details?: P) { this.status = status; this.data = { error: message, details }; } toJSON() { return { status: this.status, data: this.data }; } throw() { throw this; } } export class ResponseBadRequest extends ResponseError<400, T> { constructor(message: string, details?: T) { super(400, message, details); } } export class ResponseUnauthorized extends ResponseError<401, T> { constructor(message: string, details?: T) { super(401, message, details); } } export class ResponseForbidden extends ResponseError<403, T> { constructor(message: string, details?: T) { super(403, message, details); } } export class ResponseNotFound extends ResponseError<404, T> { constructor(message: string, details?: T) { super(404, message, details); } } export class ResponseConflict extends ResponseError<409, T> { constructor(message: string, details?: T) { super(409, message, details); } } export class ResponseUnprocessableEntity extends ResponseError<422, T> { constructor(message: string, details?: T) { super(422, message, details); } } export class ResponseTooManyRequests extends ResponseError<429, T> { constructor(message: string, details?: T) { super(429, message, details); } } export class ResponseInternalServerError extends ResponseError<500, T> { constructor(message: string, details?: T) { super(500, message, details); } } export class ResponseServiceUnavailable extends ResponseError<503, T> { constructor(message: string, details?: T) { super(503, message, details); } } export class Response { status: S; data: T; constructor(data: T, status: S) { this.status = status; this.data = data; } } export const ResponseErrorSchema = Type.Object({ error: Type.String(), details: Type.Optional(Type.Any({ default: null })) }, { $id: 'ResponseErrorSchema' }); // Simple success/failure response schemas export const ResponseSuccessSchema = Type.Object({ success: Type.Literal(true) }, { $id: 'ResponseSuccessSchema' }); export const ResponseFailureSchema = Type.Object({ success: Type.Literal(false), error: Type.Optional(Type.String()) }, { $id: 'ResponseFailureSchema' }); // Schema will be registered automatically when used in routes export const useResponseErrorSchema = (code: Code, schema?: S) => { const errorSchema = Type.Object({ error: Type.String(), details: Type.Optional(schema ?? Type.Any({ default: null })) }); const sendError =

>(message: string, details?: P) => { return new ResponseError(code, message, details); }; return { register: [code, errorSchema] as const, send: sendError }; }; export const useResponseSchema = (code: Code, schema: S) => { const sendSuccess =

>(data: P) => { return new Response(data, code); }; return { register: [code, schema] as const, send: sendSuccess }; }; export const buildResponseCodes = ( successSchema: S, errorSchema?: E ) => { const defaultErrorSchema = errorSchema ?? ResponseErrorSchema; return { 200: successSchema, 400: defaultErrorSchema, 401: defaultErrorSchema, 403: defaultErrorSchema, 404: defaultErrorSchema, 409: defaultErrorSchema, 422: defaultErrorSchema, 429: defaultErrorSchema, 500: defaultErrorSchema, 503: defaultErrorSchema } as const; }; export const buildExtraResponseCodes = ( successSchema: S, errorSchema?: E ) => { const defaultErrorSchema = errorSchema ?? ResponseErrorSchema; return { 200: successSchema, 400: defaultErrorSchema, 401: defaultErrorSchema, 403: defaultErrorSchema } } export const useResponseSchemas = ( successSchema: S, errorSchema?: E ) => { const { register: errorRegister, send: sendError } = useResponseErrorSchema(400, errorSchema); const { register: successRegister, send: sendSuccess } = useResponseSchema(200, successSchema); const send: | Static>(data: T) => ReturnType | ReturnType = (data) => { if (typeof data === 'object' && 'error' in data) { const error = data as { error: string, details?: Static }; return sendError(error.error, error.details ?? undefined) as ReturnType; } if (data instanceof Response) { return sendSuccess(data.data) as ReturnType; } else if (data instanceof ResponseError) { return sendError(data.data.error, data.data.details) as ReturnType; } } return { codes: { 200: successRegister[1], 400: errorRegister[1], 401: errorRegister[1], 403: errorRegister[1] }, sendError, sendSuccess, send }; }; // Response helpers export const responseSuccess = (data: T) => new Response(data, 200 as const); export const response200 = (data: T) => new Response(data, 200 as const); export const response201 = (data: T) => new Response(data, 201 as const); export const response202 = (data: T) => new Response(data, 202 as const); export const response204 = () => new Response(null, 204 as const); export const responseNull = () => new Response(null, 204 as const); export const responseError = (message: string, details?: T) => new ResponseError(400, message, details); // Simple success/failure helpers // Returns { success: true } with status 200 export const responseOk = () => new Response({ success: true }, 200 as const); // Returns { success: false } with status 400 // Optionally includes error message: { success: false, error: "message" } export const responseFail = (error?: string) => { const data = error ? { success: false, error } : { success: false }; return new Response(data, 400 as const); }; // Response error with details export const response400 =

(message: string, details?: P) => { return new ResponseError(400, message, details); }; export const response401 =

(message: string, details?: P) => { return new ResponseError(401, message, details); }; export const response403 =

(message: string, details?: P) => { return new ResponseError(403, message, details); }; export const response404 =

(message: string, details?: P) => { return new ResponseError(404, message, details); }; export const response409 =

(message: string, details?: P) => { return new ResponseError(409, message, details); }; export const response422 =

(message: string, details?: P) => { return new ResponseError(422, message, details); }; export const response429 =

(message: string, details?: P) => { return new ResponseError(429, message, details); }; export const response500 =

(message: string, details?: P) => { return new ResponseError(500, message, details); }; export const response503 =

(message: string, details?: P) => { return new ResponseError(503, message, details); }; export const responseForbidden =

(message: string, details?: Static

) => new ResponseError(403, message, details); export const responseNotFound =

(message: string, details?: Static

) => new ResponseError(404, message, details); export const responseConflict =

(message: string, details?: Static

) => new ResponseError(409, message, details); export const responseUnprocessableEntity =

(message: string, details?: Static

) => new ResponseError(422, message, details); export const responseTooManyRequests =

(message: string, details?: Static

) => new ResponseError(429, message, details); export const responseBadRequest =

(message: string, details?: Static

) => new ResponseError(400, message, details);