refactor progress
This commit is contained in:
30
packages/errors/package.json
Normal file
30
packages/errors/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "@fluxer/errors",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./*": "./*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluxer/config": "workspace:*",
|
||||
"@fluxer/constants": "workspace:*",
|
||||
"@fluxer/hono_types": "workspace:*",
|
||||
"@fluxer/i18n": "workspace:*",
|
||||
"@fluxer/logger": "workspace:*",
|
||||
"@fluxer/sentry": "workspace:*",
|
||||
"@fluxer/telemetry": "workspace:*",
|
||||
"hono": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"vite-tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
}
|
||||
}
|
||||
35
packages/errors/src/CaptchaErrors.tsx
Normal file
35
packages/errors/src/CaptchaErrors.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CaptchaRequiredError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CAPTCHA_REQUIRED});
|
||||
this.name = 'CaptchaRequiredError';
|
||||
}
|
||||
}
|
||||
|
||||
export class InvalidCaptchaError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_CAPTCHA});
|
||||
this.name = 'InvalidCaptchaError';
|
||||
}
|
||||
}
|
||||
98
packages/errors/src/ErrorHandler.tsx
Normal file
98
packages/errors/src/ErrorHandler.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {createJsonErrorResponse, createXmlErrorResponse} from '@fluxer/errors/src/error_handling/ErrorResponse';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import type {Context, ErrorHandler} from 'hono';
|
||||
import {HTTPException} from 'hono/http-exception';
|
||||
|
||||
const HTTP_STATUS_TO_ERROR_CODE: Record<number, string> = {
|
||||
400: 'BAD_REQUEST',
|
||||
403: 'FORBIDDEN',
|
||||
404: 'NOT_FOUND',
|
||||
405: 'METHOD_NOT_ALLOWED',
|
||||
409: 'CONFLICT',
|
||||
410: 'GONE',
|
||||
500: 'INTERNAL_SERVER_ERROR',
|
||||
501: 'NOT_IMPLEMENTED',
|
||||
502: 'BAD_GATEWAY',
|
||||
503: 'SERVICE_UNAVAILABLE',
|
||||
504: 'GATEWAY_TIMEOUT',
|
||||
};
|
||||
|
||||
export type ResponseFormat = 'json' | 'xml';
|
||||
export interface ErrorHandlerOptions {
|
||||
logError?: (error: Error, context: Context) => void;
|
||||
includeStack?: boolean;
|
||||
responseFormat?: ResponseFormat;
|
||||
customHandler?: (error: Error, context: Context) => Response | Promise<Response> | undefined;
|
||||
}
|
||||
|
||||
export function createErrorHandler(options: ErrorHandlerOptions = {}): ErrorHandler {
|
||||
const {logError, includeStack = false, responseFormat = 'json', customHandler} = options;
|
||||
|
||||
return async (error: Error, c: Context): Promise<Response> => {
|
||||
if (logError) {
|
||||
logError(error, c);
|
||||
}
|
||||
|
||||
if (customHandler) {
|
||||
const customResponse = await customHandler(error, c);
|
||||
if (customResponse) {
|
||||
return customResponse;
|
||||
}
|
||||
}
|
||||
|
||||
if (error instanceof FluxerError) {
|
||||
return error.getResponse();
|
||||
}
|
||||
|
||||
if (error instanceof HTTPException) {
|
||||
const status = error.status;
|
||||
const code = HTTP_STATUS_TO_ERROR_CODE[status] ?? 'GENERAL_ERROR';
|
||||
const message = error.message || 'An error occurred';
|
||||
|
||||
if (responseFormat === 'xml') {
|
||||
return createXmlErrorResponse(status, code, message);
|
||||
}
|
||||
|
||||
return createJsonErrorResponse({
|
||||
status,
|
||||
code,
|
||||
message,
|
||||
data: includeStack ? {stack: error.stack} : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
const message = includeStack ? error.message : 'Something went wrong. Please try again later.';
|
||||
|
||||
if (responseFormat === 'xml') {
|
||||
return createXmlErrorResponse(status, 'INTERNAL_SERVER_ERROR', message);
|
||||
}
|
||||
|
||||
return createJsonErrorResponse({
|
||||
status,
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message,
|
||||
data: includeStack ? {stack: error.stack} : undefined,
|
||||
});
|
||||
};
|
||||
}
|
||||
78
packages/errors/src/FluxerError.tsx
Normal file
78
packages/errors/src/FluxerError.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HTTPException} from 'hono/http-exception';
|
||||
|
||||
export type FluxerErrorData = Record<string, unknown>;
|
||||
export type FluxerErrorStatus = HTTPException['status'];
|
||||
export interface FluxerErrorOptions {
|
||||
code: string;
|
||||
message?: string;
|
||||
status: FluxerErrorStatus;
|
||||
data?: FluxerErrorData;
|
||||
headers?: Record<string, string>;
|
||||
messageVariables?: Record<string, unknown>;
|
||||
cause?: Error;
|
||||
}
|
||||
|
||||
export class FluxerError extends HTTPException {
|
||||
readonly code: string;
|
||||
override readonly message: string;
|
||||
override readonly status: FluxerErrorStatus;
|
||||
readonly data?: FluxerErrorData;
|
||||
readonly headers?: Record<string, string>;
|
||||
readonly messageVariables?: Record<string, unknown>;
|
||||
|
||||
constructor(options: FluxerErrorOptions) {
|
||||
const resolvedMessage = options.message ?? options.code;
|
||||
super(options.status, {message: resolvedMessage, cause: options.cause});
|
||||
this.code = options.code;
|
||||
this.message = resolvedMessage;
|
||||
this.status = options.status;
|
||||
this.data = options.data;
|
||||
this.headers = options.headers;
|
||||
this.messageVariables = options.messageVariables;
|
||||
this.name = 'FluxerError';
|
||||
}
|
||||
|
||||
override getResponse(): Response {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: this.code,
|
||||
message: this.message,
|
||||
...this.data,
|
||||
}),
|
||||
{
|
||||
status: this.status,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...this.headers,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
toJSON(): Record<string, unknown> {
|
||||
return {
|
||||
code: this.code,
|
||||
message: this.message,
|
||||
...this.data,
|
||||
};
|
||||
}
|
||||
}
|
||||
198
packages/errors/src/HttpErrors.tsx
Normal file
198
packages/errors/src/HttpErrors.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {FluxerError, type FluxerErrorData} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
interface HttpErrorOptions {
|
||||
code?: string;
|
||||
message?: string;
|
||||
data?: FluxerErrorData;
|
||||
headers?: Record<string, string>;
|
||||
cause?: Error;
|
||||
}
|
||||
|
||||
export class BadRequestError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.BAD_REQUEST,
|
||||
message: options.message ?? 'Bad Request',
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'BadRequestError';
|
||||
}
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.UNAUTHORIZED,
|
||||
message: options.message ?? 'Unauthorized',
|
||||
status: HttpStatus.UNAUTHORIZED,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'UnauthorizedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ForbiddenError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.FORBIDDEN,
|
||||
message: options.message ?? 'Forbidden',
|
||||
status: HttpStatus.FORBIDDEN,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'ForbiddenError';
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFoundError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.NOT_FOUND,
|
||||
message: options.message ?? 'Not Found',
|
||||
status: HttpStatus.NOT_FOUND,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'NotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
export class MethodNotAllowedError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.METHOD_NOT_ALLOWED,
|
||||
message: options.message ?? 'Method Not Allowed',
|
||||
status: HttpStatus.METHOD_NOT_ALLOWED,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'MethodNotAllowedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ConflictError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.CONFLICT,
|
||||
message: options.message ?? 'Conflict',
|
||||
status: HttpStatus.CONFLICT,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'ConflictError';
|
||||
}
|
||||
}
|
||||
|
||||
export class GoneError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.GONE,
|
||||
message: options.message ?? 'Gone',
|
||||
status: HttpStatus.GONE,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'GoneError';
|
||||
}
|
||||
}
|
||||
|
||||
export class InternalServerError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.INTERNAL_SERVER_ERROR,
|
||||
message: options.message ?? 'Internal Server Error',
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'InternalServerError';
|
||||
}
|
||||
}
|
||||
|
||||
export class NotImplementedError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.NOT_IMPLEMENTED,
|
||||
message: options.message ?? 'Not Implemented',
|
||||
status: HttpStatus.NOT_IMPLEMENTED,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'NotImplementedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ServiceUnavailableError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.SERVICE_UNAVAILABLE,
|
||||
message: options.message ?? 'Service Unavailable',
|
||||
status: HttpStatus.SERVICE_UNAVAILABLE,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'ServiceUnavailableError';
|
||||
}
|
||||
}
|
||||
|
||||
export class BadGatewayError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.BAD_GATEWAY,
|
||||
message: options.message ?? 'Bad Gateway',
|
||||
status: HttpStatus.BAD_GATEWAY,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'BadGatewayError';
|
||||
}
|
||||
}
|
||||
|
||||
export class GatewayTimeoutError extends FluxerError {
|
||||
constructor(options: HttpErrorOptions = {}) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.GATEWAY_TIMEOUT,
|
||||
message: options.message ?? 'Gateway Timeout',
|
||||
status: HttpStatus.GATEWAY_TIMEOUT,
|
||||
data: options.data,
|
||||
headers: options.headers,
|
||||
cause: options.cause,
|
||||
});
|
||||
this.name = 'GatewayTimeoutError';
|
||||
}
|
||||
}
|
||||
75
packages/errors/src/ValidationError.tsx
Normal file
75
packages/errors/src/ValidationError.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export interface FieldError {
|
||||
field: string;
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ValidationErrorOptions {
|
||||
code?: string;
|
||||
message?: string;
|
||||
errors: Array<FieldError>;
|
||||
}
|
||||
|
||||
export class ValidationError extends FluxerError {
|
||||
readonly errors: Array<FieldError>;
|
||||
|
||||
constructor(options: ValidationErrorOptions) {
|
||||
super({
|
||||
code: options.code ?? APIErrorCodes.VALIDATION_ERROR,
|
||||
message: options.message ?? 'Validation failed',
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
data: {errors: options.errors},
|
||||
});
|
||||
this.name = 'ValidationError';
|
||||
this.errors = options.errors;
|
||||
}
|
||||
|
||||
override getResponse(): Response {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: this.code,
|
||||
message: this.message,
|
||||
errors: this.errors,
|
||||
}),
|
||||
{
|
||||
status: this.status,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static fromField(field: string, code: string, message: string): ValidationError {
|
||||
return new ValidationError({
|
||||
errors: [{field, code, message}],
|
||||
});
|
||||
}
|
||||
|
||||
static fromFields(errors: Array<FieldError>): ValidationError {
|
||||
return new ValidationError({errors});
|
||||
}
|
||||
}
|
||||
101
packages/errors/src/__tests__/AppErrorHandlers.test.tsx
Normal file
101
packages/errors/src/__tests__/AppErrorHandlers.test.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {Locales} from '@fluxer/constants/src/Locales';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
import {AppErrorHandler} from '@fluxer/errors/src/domains/core/ErrorHandlers';
|
||||
import type {BaseHonoEnv} from '@fluxer/hono_types/src/HonoTypes';
|
||||
import {Hono} from 'hono';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
interface ErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
function createApp(): Hono<BaseHonoEnv> {
|
||||
const app = new Hono<BaseHonoEnv>();
|
||||
app.onError(AppErrorHandler);
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('AppErrorHandler i18n fallbacks', () => {
|
||||
it('localizes unexpected errors from Accept-Language when middleware locale is missing', async () => {
|
||||
const app = createApp();
|
||||
|
||||
app.get('/test', () => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
|
||||
const response = await app.request('/test', {
|
||||
headers: {
|
||||
'accept-language': 'fr-CA,fr;q=0.9,en;q=0.8',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe(APIErrorCodes.INTERNAL_SERVER_ERROR);
|
||||
expect(body.message).toBe('Erreur interne du serveur.');
|
||||
});
|
||||
|
||||
it('localizes FluxerError responses without errorI18nService in context', async () => {
|
||||
const app = createApp();
|
||||
|
||||
app.get('/test', () => {
|
||||
throw new BadRequestError({code: APIErrorCodes.BAD_REQUEST});
|
||||
});
|
||||
|
||||
const response = await app.request('/test', {
|
||||
headers: {
|
||||
'accept-language': 'fr',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe(APIErrorCodes.BAD_REQUEST);
|
||||
expect(body.message).toBe('Requête invalide.');
|
||||
});
|
||||
|
||||
it('prefers requestLocale context over Accept-Language header', async () => {
|
||||
const app = createApp();
|
||||
|
||||
app.use('*', async (ctx, next) => {
|
||||
ctx.set('requestLocale', Locales.EN_US);
|
||||
await next();
|
||||
});
|
||||
|
||||
app.get('/test', () => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
|
||||
const response = await app.request('/test', {
|
||||
headers: {
|
||||
'accept-language': 'fr',
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe(APIErrorCodes.INTERNAL_SERVER_ERROR);
|
||||
expect(body.message).toBe('Internal server error.');
|
||||
});
|
||||
});
|
||||
122
packages/errors/src/__tests__/CaptchaErrors.test.tsx
Normal file
122
packages/errors/src/__tests__/CaptchaErrors.test.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {CaptchaRequiredError, InvalidCaptchaError} from '@fluxer/errors/src/CaptchaErrors';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {ErrorCodeToI18nKey} from '@fluxer/errors/src/i18n/ErrorCodeMappings';
|
||||
import {getErrorMessage} from '@fluxer/errors/src/i18n/ErrorI18n';
|
||||
import type {ErrorI18nKey} from '@fluxer/errors/src/i18n/ErrorI18nTypes.generated';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('CaptchaErrors', () => {
|
||||
describe('CaptchaRequiredError', () => {
|
||||
it('should have correct code and name', () => {
|
||||
const error = new CaptchaRequiredError();
|
||||
|
||||
expect(error.code).toBe('CAPTCHA_REQUIRED');
|
||||
expect(error.name).toBe('CaptchaRequiredError');
|
||||
});
|
||||
|
||||
it('should have status 400', () => {
|
||||
const error = new CaptchaRequiredError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
it('should extend BadRequestError', () => {
|
||||
const error = new CaptchaRequiredError();
|
||||
|
||||
expect(error).toBeInstanceOf(BadRequestError);
|
||||
});
|
||||
|
||||
it('should extend FluxerError', () => {
|
||||
const error = new CaptchaRequiredError();
|
||||
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
|
||||
it('should have an i18n mapping that resolves to the correct message', () => {
|
||||
const error = new CaptchaRequiredError();
|
||||
const i18nKey = ErrorCodeToI18nKey[error.code as keyof typeof ErrorCodeToI18nKey] as ErrorI18nKey;
|
||||
|
||||
expect(i18nKey).toBe('captcha.required');
|
||||
expect(getErrorMessage(i18nKey, 'en-US')).toBe('Captcha is required.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('InvalidCaptchaError', () => {
|
||||
it('should have correct code and name', () => {
|
||||
const error = new InvalidCaptchaError();
|
||||
|
||||
expect(error.code).toBe('INVALID_CAPTCHA');
|
||||
expect(error.name).toBe('InvalidCaptchaError');
|
||||
});
|
||||
|
||||
it('should have status 400', () => {
|
||||
const error = new InvalidCaptchaError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
it('should extend BadRequestError', () => {
|
||||
const error = new InvalidCaptchaError();
|
||||
|
||||
expect(error).toBeInstanceOf(BadRequestError);
|
||||
});
|
||||
|
||||
it('should extend FluxerError', () => {
|
||||
const error = new InvalidCaptchaError();
|
||||
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
|
||||
it('should have an i18n mapping that resolves to the correct message', () => {
|
||||
const error = new InvalidCaptchaError();
|
||||
const i18nKey = ErrorCodeToI18nKey[error.code as keyof typeof ErrorCodeToI18nKey] as ErrorI18nKey;
|
||||
|
||||
expect(i18nKey).toBe('captcha.invalid');
|
||||
expect(getErrorMessage(i18nKey, 'en-US')).toBe('Invalid captcha.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error differentiation', () => {
|
||||
it('should have different codes for required vs invalid', () => {
|
||||
const requiredError = new CaptchaRequiredError();
|
||||
const invalidError = new InvalidCaptchaError();
|
||||
|
||||
expect(requiredError.code).not.toBe(invalidError.code);
|
||||
});
|
||||
|
||||
it('should have different i18n messages for required vs invalid', () => {
|
||||
const requiredKey = ErrorCodeToI18nKey['CAPTCHA_REQUIRED'] as ErrorI18nKey;
|
||||
const invalidKey = ErrorCodeToI18nKey['INVALID_CAPTCHA'] as ErrorI18nKey;
|
||||
|
||||
expect(getErrorMessage(requiredKey, 'en-US')).not.toBe(getErrorMessage(invalidKey, 'en-US'));
|
||||
});
|
||||
|
||||
it('should have different names for required vs invalid', () => {
|
||||
const requiredError = new CaptchaRequiredError();
|
||||
const invalidError = new InvalidCaptchaError();
|
||||
|
||||
expect(requiredError.name).not.toBe(invalidError.name);
|
||||
});
|
||||
});
|
||||
});
|
||||
379
packages/errors/src/__tests__/DomainErrors.test.tsx
Normal file
379
packages/errors/src/__tests__/DomainErrors.test.tsx
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {ValidationErrorCodes} from '@fluxer/constants/src/ValidationErrorCodes';
|
||||
import {InvalidPhoneNumberError} from '@fluxer/errors/src/domains/auth/InvalidPhoneNumberError';
|
||||
import {UnknownChannelError} from '@fluxer/errors/src/domains/channel/UnknownChannelError';
|
||||
import {UnknownMessageError} from '@fluxer/errors/src/domains/channel/UnknownMessageError';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidationError';
|
||||
import {InternalServerError} from '@fluxer/errors/src/domains/core/InternalServerError';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
interface ErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
describe('Domain Errors', () => {
|
||||
describe('core domain errors', () => {
|
||||
describe('BadRequestError', () => {
|
||||
it('should create error with required code', () => {
|
||||
const error = new BadRequestError({code: APIErrorCodes.INVALID_REQUEST});
|
||||
|
||||
expect(error.status).toBe(400);
|
||||
expect(error.code).toBe(APIErrorCodes.INVALID_REQUEST);
|
||||
expect(error.message).toBe(APIErrorCodes.INVALID_REQUEST);
|
||||
});
|
||||
|
||||
it('should allow custom message', () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_FORM_BODY,
|
||||
message: 'Custom bad request message',
|
||||
});
|
||||
|
||||
expect(error.message).toBe('Custom bad request message');
|
||||
});
|
||||
|
||||
it('should include data', () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
data: {field: 'test'},
|
||||
});
|
||||
|
||||
expect(error.data).toEqual({field: 'test'});
|
||||
});
|
||||
|
||||
it('should include headers', () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
headers: {'X-Custom': 'value'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({'X-Custom': 'value'});
|
||||
});
|
||||
|
||||
it('should include messageVariables for i18n', () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
messageVariables: {count: 5},
|
||||
});
|
||||
|
||||
expect(error.messageVariables).toEqual({count: 5});
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new BadRequestError({code: APIErrorCodes.INVALID_REQUEST});
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NotFoundError', () => {
|
||||
it('should create error with status 404', () => {
|
||||
const error = new NotFoundError({code: APIErrorCodes.UNKNOWN_USER});
|
||||
|
||||
expect(error.status).toBe(404);
|
||||
expect(error.code).toBe(APIErrorCodes.UNKNOWN_USER);
|
||||
expect(error.message).toBe(APIErrorCodes.UNKNOWN_USER);
|
||||
});
|
||||
|
||||
it('should allow messageVariables for i18n', () => {
|
||||
const error = new NotFoundError({
|
||||
code: APIErrorCodes.UNKNOWN_USER,
|
||||
messageVariables: {userId: '12345'},
|
||||
});
|
||||
|
||||
expect(error.messageVariables).toEqual({userId: '12345'});
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new NotFoundError({code: APIErrorCodes.UNKNOWN_USER});
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ForbiddenError', () => {
|
||||
it('should create error with status 403', () => {
|
||||
const error = new ForbiddenError({code: APIErrorCodes.ACCESS_DENIED});
|
||||
|
||||
expect(error.status).toBe(403);
|
||||
expect(error.code).toBe(APIErrorCodes.ACCESS_DENIED);
|
||||
expect(error.message).toBe(APIErrorCodes.ACCESS_DENIED);
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new ForbiddenError({code: APIErrorCodes.ACCESS_DENIED});
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('InternalServerError', () => {
|
||||
it('should create error with status 500', () => {
|
||||
const error = new InternalServerError({code: APIErrorCodes.GENERAL_ERROR});
|
||||
|
||||
expect(error.status).toBe(500);
|
||||
expect(error.code).toBe(APIErrorCodes.GENERAL_ERROR);
|
||||
expect(error.message).toBe(APIErrorCodes.GENERAL_ERROR);
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new InternalServerError({code: APIErrorCodes.GENERAL_ERROR});
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('InputValidationError', () => {
|
||||
it('should create error with validation errors', () => {
|
||||
const error = new InputValidationError([{path: 'email', message: 'Invalid email format'}]);
|
||||
|
||||
expect(error.status).toBe(400);
|
||||
expect(error.code).toBe(APIErrorCodes.INVALID_FORM_BODY);
|
||||
expect(error.data).toEqual({
|
||||
errors: [{path: 'email', message: 'Invalid email format'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should support localized errors', () => {
|
||||
const error = new InputValidationError(
|
||||
[{path: 'name', message: ValidationErrorCodes.EMAIL_IS_REQUIRED}],
|
||||
[{path: 'name', code: ValidationErrorCodes.EMAIL_IS_REQUIRED}],
|
||||
);
|
||||
|
||||
expect(error.localizedErrors).toEqual([{path: 'name', code: ValidationErrorCodes.EMAIL_IS_REQUIRED}]);
|
||||
expect(error.getLocalizedErrors()).toEqual([{path: 'name', code: ValidationErrorCodes.EMAIL_IS_REQUIRED}]);
|
||||
});
|
||||
|
||||
it('should return null for localizedErrors when not provided', () => {
|
||||
const error = new InputValidationError([{path: 'field', message: 'error'}]);
|
||||
|
||||
expect(error.localizedErrors).toBeNull();
|
||||
expect(error.getLocalizedErrors()).toBeNull();
|
||||
});
|
||||
|
||||
it('should create from single field using static method', () => {
|
||||
const error = InputValidationError.create('username', 'Username is required');
|
||||
|
||||
expect(error).toBeInstanceOf(InputValidationError);
|
||||
expect(error.data).toEqual({
|
||||
errors: [{path: 'username', message: 'Username is required'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create from multiple fields using static method', () => {
|
||||
const error = InputValidationError.createMultiple([
|
||||
{field: 'email', message: 'Invalid email'},
|
||||
{field: 'password', message: 'Password too short'},
|
||||
]);
|
||||
|
||||
expect(error.data).toEqual({
|
||||
errors: [
|
||||
{path: 'email', message: 'Invalid email'},
|
||||
{path: 'password', message: 'Password too short'},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create from error code using static method', () => {
|
||||
const error = InputValidationError.fromCode('email', ValidationErrorCodes.EMAIL_IS_REQUIRED, {maxLength: 255});
|
||||
|
||||
expect(error.localizedErrors).toEqual([
|
||||
{path: 'email', code: ValidationErrorCodes.EMAIL_IS_REQUIRED, variables: {maxLength: 255}},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create from multiple error codes using static method', () => {
|
||||
const error = InputValidationError.fromCodes([
|
||||
{path: 'email', code: ValidationErrorCodes.EMAIL_IS_REQUIRED},
|
||||
{path: 'name', code: ValidationErrorCodes.STRING_LENGTH_INVALID, variables: {max: 100}},
|
||||
]);
|
||||
|
||||
expect(error.localizedErrors).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('auth domain errors', () => {
|
||||
describe('InvalidPhoneNumberError', () => {
|
||||
it('should have correct code from APIErrorCodes', () => {
|
||||
const error = new InvalidPhoneNumberError();
|
||||
|
||||
expect(error.code).toBe(APIErrorCodes.INVALID_PHONE_NUMBER);
|
||||
expect(error.status).toBe(HttpStatus.BAD_REQUEST);
|
||||
});
|
||||
|
||||
it('should be instance of BadRequestError', () => {
|
||||
const error = new InvalidPhoneNumberError();
|
||||
expect(error).toBeInstanceOf(BadRequestError);
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new InvalidPhoneNumberError();
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('channel domain errors', () => {
|
||||
describe('UnknownChannelError', () => {
|
||||
it('should have correct code from APIErrorCodes', () => {
|
||||
const error = new UnknownChannelError();
|
||||
|
||||
expect(error.code).toBe(APIErrorCodes.UNKNOWN_CHANNEL);
|
||||
expect(error.status).toBe(HttpStatus.NOT_FOUND);
|
||||
});
|
||||
|
||||
it('should be instance of NotFoundError', () => {
|
||||
const error = new UnknownChannelError();
|
||||
expect(error).toBeInstanceOf(NotFoundError);
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new UnknownChannelError();
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UnknownMessageError', () => {
|
||||
it('should have correct code from APIErrorCodes', () => {
|
||||
const error = new UnknownMessageError();
|
||||
|
||||
expect(error.code).toBe(APIErrorCodes.UNKNOWN_MESSAGE);
|
||||
expect(error.status).toBe(HttpStatus.NOT_FOUND);
|
||||
});
|
||||
|
||||
it('should be instance of NotFoundError', () => {
|
||||
const error = new UnknownMessageError();
|
||||
expect(error).toBeInstanceOf(NotFoundError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('error response generation', () => {
|
||||
it('should generate correct JSON response for domain errors', async () => {
|
||||
const error = new UnknownChannelError();
|
||||
const response = error.getResponse();
|
||||
|
||||
expect(response.status).toBe(404);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe(APIErrorCodes.UNKNOWN_CHANNEL);
|
||||
});
|
||||
|
||||
it('should include data in response', async () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
data: {field: 'test', reason: 'invalid'},
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(body).toEqual({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
message: APIErrorCodes.INVALID_REQUEST,
|
||||
field: 'test',
|
||||
reason: 'invalid',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include custom headers in response', async () => {
|
||||
const error = new ForbiddenError({
|
||||
code: APIErrorCodes.ACCESS_DENIED,
|
||||
headers: {'X-Permission-Required': 'admin'},
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
|
||||
expect(response.headers.get('X-Permission-Required')).toBe('admin');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error serialization', () => {
|
||||
it('should serialize domain errors to JSON correctly', () => {
|
||||
const error = new UnknownChannelError();
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).toEqual({
|
||||
code: APIErrorCodes.UNKNOWN_CHANNEL,
|
||||
message: APIErrorCodes.UNKNOWN_CHANNEL,
|
||||
});
|
||||
});
|
||||
|
||||
it('should include data in JSON serialization', () => {
|
||||
const error = new BadRequestError({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
data: {extra: 'info'},
|
||||
});
|
||||
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).toEqual({
|
||||
code: APIErrorCodes.INVALID_REQUEST,
|
||||
message: APIErrorCodes.INVALID_REQUEST,
|
||||
extra: 'info',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('error inheritance chain', () => {
|
||||
it('should maintain correct prototype chain', () => {
|
||||
const error = new InvalidPhoneNumberError();
|
||||
|
||||
expect(error).toBeInstanceOf(InvalidPhoneNumberError);
|
||||
expect(error).toBeInstanceOf(BadRequestError);
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
});
|
||||
|
||||
it('should be catchable at any level of the chain', () => {
|
||||
const error = new UnknownChannelError();
|
||||
|
||||
try {
|
||||
throw error;
|
||||
} catch (e) {
|
||||
if (e instanceof NotFoundError) {
|
||||
expect(e.code).toBe(APIErrorCodes.UNKNOWN_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
throw error;
|
||||
} catch (e) {
|
||||
if (e instanceof FluxerError) {
|
||||
expect(e.status).toBe(404);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
throw error;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
expect(e).toBeInstanceOf(UnknownChannelError);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
359
packages/errors/src/__tests__/ErrorHandler.test.tsx
Normal file
359
packages/errors/src/__tests__/ErrorHandler.test.tsx
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {createErrorHandler, type ErrorHandlerOptions} from '@fluxer/errors/src/ErrorHandler';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {Hono} from 'hono';
|
||||
import {HTTPException} from 'hono/http-exception';
|
||||
import {describe, expect, it, vi} from 'vitest';
|
||||
|
||||
interface ErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
function createTestApp(options: ErrorHandlerOptions = {}) {
|
||||
const app = new Hono();
|
||||
app.onError(createErrorHandler(options));
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('createErrorHandler', () => {
|
||||
describe('FluxerError handling', () => {
|
||||
it('should return FluxerError response directly', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test error message',
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body).toEqual({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test error message',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include FluxerError data in response', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new FluxerError({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
status: 400,
|
||||
data: {field: 'email'},
|
||||
});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(body).toEqual({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
field: 'email',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include FluxerError custom headers', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new FluxerError({
|
||||
code: 'RATE_LIMITED',
|
||||
status: 429,
|
||||
headers: {'Retry-After': '60'},
|
||||
});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.headers.get('Retry-After')).toBe('60');
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTTPException handling', () => {
|
||||
it('should handle HTTPException with JSON response', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new HTTPException(403, {message: 'Access denied'});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe('FORBIDDEN');
|
||||
expect(body.message).toBe('Access denied');
|
||||
});
|
||||
|
||||
it('should use default message for HTTPException without message', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new HTTPException(500);
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.message).toBe('An error occurred');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generic Error handling', () => {
|
||||
it('should return 500 for generic errors', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new Error('Something went wrong');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe('INTERNAL_SERVER_ERROR');
|
||||
expect(body.message).toBe('Something went wrong. Please try again later.');
|
||||
});
|
||||
|
||||
it('should not expose error message without includeStack option', async () => {
|
||||
const app = createTestApp({includeStack: false});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Sensitive error details');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(body.message).toBe('Something went wrong. Please try again later.');
|
||||
expect(body.message).not.toContain('Sensitive');
|
||||
});
|
||||
|
||||
it('should expose error message with includeStack option', async () => {
|
||||
const app = createTestApp({includeStack: true});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Error details for debugging');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(body.message).toBe('Error details for debugging');
|
||||
});
|
||||
|
||||
it('should include stack trace with includeStack option', async () => {
|
||||
const app = createTestApp({includeStack: true});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Test error');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(body).toHaveProperty('stack');
|
||||
expect(body.stack).toContain('Error: Test error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('logError callback', () => {
|
||||
it('should call logError with error and context', async () => {
|
||||
const logError = vi.fn();
|
||||
const app = createTestApp({logError});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Logged error');
|
||||
});
|
||||
|
||||
await app.request('/test');
|
||||
|
||||
expect(logError).toHaveBeenCalledTimes(1);
|
||||
expect(logError.mock.calls[0][0]).toBeInstanceOf(Error);
|
||||
expect((logError.mock.calls[0][0] as Error).message).toBe('Logged error');
|
||||
});
|
||||
|
||||
it('should call logError for FluxerError', async () => {
|
||||
const logError = vi.fn();
|
||||
const app = createTestApp({logError});
|
||||
app.get('/test', () => {
|
||||
throw new FluxerError({code: 'TEST', status: 400});
|
||||
});
|
||||
|
||||
await app.request('/test');
|
||||
|
||||
expect(logError).toHaveBeenCalledTimes(1);
|
||||
expect(logError.mock.calls[0][0]).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('customHandler callback', () => {
|
||||
it('should use customHandler response when provided', async () => {
|
||||
const customHandler = vi.fn().mockReturnValue(
|
||||
new Response(JSON.stringify({custom: true}), {
|
||||
status: 418,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
}),
|
||||
);
|
||||
const app = createTestApp({customHandler});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Custom handled');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(418);
|
||||
const body = (await response.json()) as {custom: boolean};
|
||||
expect(body).toEqual({custom: true});
|
||||
expect(customHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should fall back to default handling when customHandler returns undefined', async () => {
|
||||
const customHandler = vi.fn().mockReturnValue(undefined);
|
||||
const app = createTestApp({customHandler});
|
||||
app.get('/test', () => {
|
||||
throw new FluxerError({code: 'FALLBACK', status: 400});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
expect(body.code).toBe('FALLBACK');
|
||||
});
|
||||
|
||||
it('should support async customHandler', async () => {
|
||||
const customHandler = vi.fn().mockResolvedValue(
|
||||
new Response(JSON.stringify({async: true}), {
|
||||
status: 202,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
}),
|
||||
);
|
||||
const app = createTestApp({customHandler});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Async handled');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(202);
|
||||
const body = (await response.json()) as {async: boolean};
|
||||
expect(body).toEqual({async: true});
|
||||
});
|
||||
});
|
||||
|
||||
describe('responseFormat option', () => {
|
||||
it('should return JSON by default', async () => {
|
||||
const app = createTestApp();
|
||||
app.get('/test', () => {
|
||||
throw new HTTPException(400);
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
});
|
||||
|
||||
it('should return XML when responseFormat is xml', async () => {
|
||||
const app = createTestApp({responseFormat: 'xml'});
|
||||
app.get('/test', () => {
|
||||
throw new HTTPException(400, {message: 'Bad request'});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/xml');
|
||||
|
||||
const body = await response.text();
|
||||
expect(body).toContain('<?xml version="1.0"');
|
||||
expect(body).toContain('<Error>');
|
||||
expect(body).toContain('<Code>BAD_REQUEST</Code>');
|
||||
expect(body).toContain('<Message>Bad request</Message>');
|
||||
});
|
||||
|
||||
it('should escape XML special characters', async () => {
|
||||
const app = createTestApp({responseFormat: 'xml'});
|
||||
app.get('/test', () => {
|
||||
throw new HTTPException(400, {message: 'Error with <special> & "chars"'});
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = await response.text();
|
||||
|
||||
expect(body).toContain('<special>');
|
||||
expect(body).toContain('&');
|
||||
expect(body).toContain('"chars"');
|
||||
});
|
||||
|
||||
it('should return XML for internal errors when responseFormat is xml', async () => {
|
||||
const app = createTestApp({responseFormat: 'xml'});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Internal error');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
|
||||
expect(response.status).toBe(500);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/xml');
|
||||
|
||||
const body = await response.text();
|
||||
expect(body).toContain('<Code>INTERNAL_SERVER_ERROR</Code>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('combined options', () => {
|
||||
it('should support logError and includeStack together', async () => {
|
||||
const logError = vi.fn();
|
||||
const app = createTestApp({logError, includeStack: true});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Combined test');
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const body = (await response.json()) as ErrorResponse;
|
||||
|
||||
expect(logError).toHaveBeenCalledTimes(1);
|
||||
expect(body.message).toBe('Combined test');
|
||||
expect(body).toHaveProperty('stack');
|
||||
});
|
||||
|
||||
it('should call logError before customHandler', async () => {
|
||||
const callOrder: Array<string> = [];
|
||||
const logError = vi.fn(() => callOrder.push('log'));
|
||||
const customHandler = vi.fn(() => {
|
||||
callOrder.push('custom');
|
||||
return undefined;
|
||||
});
|
||||
const app = createTestApp({logError, customHandler});
|
||||
app.get('/test', () => {
|
||||
throw new Error('Order test');
|
||||
});
|
||||
|
||||
await app.request('/test');
|
||||
|
||||
expect(callOrder).toEqual(['log', 'custom']);
|
||||
});
|
||||
});
|
||||
});
|
||||
273
packages/errors/src/__tests__/FluxerError.test.tsx
Normal file
273
packages/errors/src/__tests__/FluxerError.test.tsx
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {HTTPException} from 'hono/http-exception';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('FluxerError', () => {
|
||||
describe('constructor', () => {
|
||||
it('should create an error with required options', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
expect(error.code).toBe('TEST_ERROR');
|
||||
expect(error.status).toBe(400);
|
||||
expect(error.message).toBe('TEST_ERROR');
|
||||
expect(error.name).toBe('FluxerError');
|
||||
});
|
||||
|
||||
it('should use provided message instead of code', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Custom error message',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
expect(error.code).toBe('TEST_ERROR');
|
||||
expect(error.message).toBe('Custom error message');
|
||||
});
|
||||
|
||||
it('should include optional data', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
status: 400,
|
||||
data: {field: 'username', reason: 'invalid'},
|
||||
});
|
||||
|
||||
expect(error.data).toEqual({field: 'username', reason: 'invalid'});
|
||||
});
|
||||
|
||||
it('should include optional headers', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
status: 400,
|
||||
headers: {'X-Custom-Header': 'value'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({'X-Custom-Header': 'value'});
|
||||
});
|
||||
|
||||
it('should include message variables for i18n', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'RATE_LIMITED',
|
||||
status: 429,
|
||||
messageVariables: {retryAfter: 60},
|
||||
});
|
||||
|
||||
expect(error.messageVariables).toEqual({retryAfter: 60});
|
||||
});
|
||||
|
||||
it('should include cause for error chaining', () => {
|
||||
const cause = new Error('Original error');
|
||||
const error = new FluxerError({
|
||||
code: 'WRAPPED_ERROR',
|
||||
status: 500,
|
||||
cause,
|
||||
});
|
||||
|
||||
expect(error.cause).toBe(cause);
|
||||
});
|
||||
|
||||
it('should be an instance of HTTPException', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
expect(error).toBeInstanceOf(HTTPException);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getResponse', () => {
|
||||
it('should return a JSON Response with correct status', async () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test message',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toEqual({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test message',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include data in response body', async () => {
|
||||
const error = new FluxerError({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
status: 400,
|
||||
data: {errors: [{field: 'email', message: 'Invalid email'}]},
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = await response.json();
|
||||
|
||||
expect(body).toEqual({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
errors: [{field: 'email', message: 'Invalid email'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should include custom headers in response', async () => {
|
||||
const error = new FluxerError({
|
||||
code: 'RATE_LIMITED',
|
||||
status: 429,
|
||||
headers: {'Retry-After': '60', 'X-RateLimit-Reset': '1234567890'},
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
|
||||
expect(response.headers.get('Retry-After')).toBe('60');
|
||||
expect(response.headers.get('X-RateLimit-Reset')).toBe('1234567890');
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
});
|
||||
|
||||
it('should handle empty data', async () => {
|
||||
const error = new FluxerError({
|
||||
code: 'SIMPLE_ERROR',
|
||||
status: 403,
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = await response.json();
|
||||
|
||||
expect(body).toEqual({
|
||||
code: 'SIMPLE_ERROR',
|
||||
message: 'SIMPLE_ERROR',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toJSON', () => {
|
||||
it('should serialize to JSON object', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test message',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).toEqual({
|
||||
code: 'TEST_ERROR',
|
||||
message: 'Test message',
|
||||
});
|
||||
});
|
||||
|
||||
it('should include data in JSON output', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
status: 400,
|
||||
data: {field: 'username'},
|
||||
});
|
||||
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).toEqual({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
field: 'username',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include status or headers in JSON output', () => {
|
||||
const error = new FluxerError({
|
||||
code: 'TEST_ERROR',
|
||||
status: 500,
|
||||
headers: {'X-Custom': 'value'},
|
||||
});
|
||||
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).not.toHaveProperty('status');
|
||||
expect(json).not.toHaveProperty('headers');
|
||||
});
|
||||
});
|
||||
|
||||
describe('status codes', () => {
|
||||
it('should handle 4xx client error status codes', () => {
|
||||
const testCases = [
|
||||
{status: 400, name: 'Bad Request'},
|
||||
{status: 401, name: 'Unauthorized'},
|
||||
{status: 403, name: 'Forbidden'},
|
||||
{status: 404, name: 'Not Found'},
|
||||
{status: 409, name: 'Conflict'},
|
||||
{status: 429, name: 'Too Many Requests'},
|
||||
] as const;
|
||||
|
||||
for (const {status} of testCases) {
|
||||
const error = new FluxerError({code: 'TEST', status});
|
||||
expect(error.status).toBe(status);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle 5xx server error status codes', () => {
|
||||
const testCases = [
|
||||
{status: 500, name: 'Internal Server Error'},
|
||||
{status: 501, name: 'Not Implemented'},
|
||||
{status: 502, name: 'Bad Gateway'},
|
||||
{status: 503, name: 'Service Unavailable'},
|
||||
{status: 504, name: 'Gateway Timeout'},
|
||||
] as const;
|
||||
|
||||
for (const {status} of testCases) {
|
||||
const error = new FluxerError({code: 'TEST', status});
|
||||
expect(error.status).toBe(status);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('error properties', () => {
|
||||
it('should have correct name property', () => {
|
||||
const error = new FluxerError({code: 'TEST', status: 400});
|
||||
expect(error.name).toBe('FluxerError');
|
||||
});
|
||||
|
||||
it('should be throwable', () => {
|
||||
const error = new FluxerError({code: 'THROWN_ERROR', status: 400});
|
||||
|
||||
expect(() => {
|
||||
throw error;
|
||||
}).toThrow(FluxerError);
|
||||
});
|
||||
|
||||
it('should be catchable as Error', () => {
|
||||
const error = new FluxerError({code: 'CAUGHT_ERROR', status: 400});
|
||||
|
||||
try {
|
||||
throw error;
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e).toBeInstanceOf(FluxerError);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
342
packages/errors/src/__tests__/HttpErrors.test.tsx
Normal file
342
packages/errors/src/__tests__/HttpErrors.test.tsx
Normal file
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {
|
||||
BadGatewayError,
|
||||
BadRequestError,
|
||||
ConflictError,
|
||||
ForbiddenError,
|
||||
GatewayTimeoutError,
|
||||
GoneError,
|
||||
InternalServerError,
|
||||
MethodNotAllowedError,
|
||||
NotFoundError,
|
||||
NotImplementedError,
|
||||
ServiceUnavailableError,
|
||||
UnauthorizedError,
|
||||
} from '@fluxer/errors/src/HttpErrors';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
describe('HttpErrors', () => {
|
||||
describe('BadRequestError', () => {
|
||||
it('should have status 400 and default code/message', () => {
|
||||
const error = new BadRequestError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(error.code).toBe('BAD_REQUEST');
|
||||
expect(error.message).toBe('Bad Request');
|
||||
expect(error.name).toBe('BadRequestError');
|
||||
});
|
||||
|
||||
it('should allow custom code', () => {
|
||||
const error = new BadRequestError({code: 'INVALID_INPUT'});
|
||||
|
||||
expect(error.code).toBe('INVALID_INPUT');
|
||||
expect(error.message).toBe('Bad Request');
|
||||
});
|
||||
|
||||
it('should allow custom message', () => {
|
||||
const error = new BadRequestError({message: 'Invalid request payload'});
|
||||
|
||||
expect(error.code).toBe('BAD_REQUEST');
|
||||
expect(error.message).toBe('Invalid request payload');
|
||||
});
|
||||
|
||||
it('should allow custom code and message', () => {
|
||||
const error = new BadRequestError({
|
||||
code: 'VALIDATION_FAILED',
|
||||
message: 'Request validation failed',
|
||||
});
|
||||
|
||||
expect(error.code).toBe('VALIDATION_FAILED');
|
||||
expect(error.message).toBe('Request validation failed');
|
||||
});
|
||||
|
||||
it('should include data', () => {
|
||||
const error = new BadRequestError({
|
||||
data: {field: 'email', reason: 'invalid format'},
|
||||
});
|
||||
|
||||
expect(error.data).toEqual({field: 'email', reason: 'invalid format'});
|
||||
});
|
||||
|
||||
it('should include headers', () => {
|
||||
const error = new BadRequestError({
|
||||
headers: {'X-Error-Type': 'validation'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({'X-Error-Type': 'validation'});
|
||||
});
|
||||
|
||||
it('should include cause', () => {
|
||||
const cause = new Error('Original error');
|
||||
const error = new BadRequestError({cause});
|
||||
|
||||
expect(error.cause).toBe(cause);
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new BadRequestError();
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UnauthorizedError', () => {
|
||||
it('should have status 401 and default code/message', () => {
|
||||
const error = new UnauthorizedError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.UNAUTHORIZED);
|
||||
expect(error.code).toBe('UNAUTHORIZED');
|
||||
expect(error.message).toBe('Unauthorized');
|
||||
expect(error.name).toBe('UnauthorizedError');
|
||||
});
|
||||
|
||||
it('should allow custom code', () => {
|
||||
const error = new UnauthorizedError({code: 'INVALID_TOKEN'});
|
||||
|
||||
expect(error.code).toBe('INVALID_TOKEN');
|
||||
});
|
||||
|
||||
it('should include WWW-Authenticate header', () => {
|
||||
const error = new UnauthorizedError({
|
||||
headers: {'WWW-Authenticate': 'Bearer realm="api"'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({'WWW-Authenticate': 'Bearer realm="api"'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ForbiddenError', () => {
|
||||
it('should have status 403 and default code/message', () => {
|
||||
const error = new ForbiddenError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.FORBIDDEN);
|
||||
expect(error.code).toBe('FORBIDDEN');
|
||||
expect(error.message).toBe('Forbidden');
|
||||
expect(error.name).toBe('ForbiddenError');
|
||||
});
|
||||
|
||||
it('should allow custom code for permission errors', () => {
|
||||
const error = new ForbiddenError({code: 'MISSING_PERMISSIONS'});
|
||||
|
||||
expect(error.code).toBe('MISSING_PERMISSIONS');
|
||||
});
|
||||
});
|
||||
|
||||
describe('NotFoundError', () => {
|
||||
it('should have status 404 and default code/message', () => {
|
||||
const error = new NotFoundError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.NOT_FOUND);
|
||||
expect(error.code).toBe('NOT_FOUND');
|
||||
expect(error.message).toBe('Not Found');
|
||||
expect(error.name).toBe('NotFoundError');
|
||||
});
|
||||
|
||||
it('should allow custom code for resource not found', () => {
|
||||
const error = new NotFoundError({code: 'UNKNOWN_USER'});
|
||||
|
||||
expect(error.code).toBe('UNKNOWN_USER');
|
||||
});
|
||||
});
|
||||
|
||||
describe('MethodNotAllowedError', () => {
|
||||
it('should have status 405 and default code/message', () => {
|
||||
const error = new MethodNotAllowedError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.METHOD_NOT_ALLOWED);
|
||||
expect(error.code).toBe('METHOD_NOT_ALLOWED');
|
||||
expect(error.message).toBe('Method Not Allowed');
|
||||
expect(error.name).toBe('MethodNotAllowedError');
|
||||
});
|
||||
|
||||
it('should include Allow header', () => {
|
||||
const error = new MethodNotAllowedError({
|
||||
headers: {Allow: 'GET, POST'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({Allow: 'GET, POST'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ConflictError', () => {
|
||||
it('should have status 409 and default code/message', () => {
|
||||
const error = new ConflictError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.CONFLICT);
|
||||
expect(error.code).toBe('CONFLICT');
|
||||
expect(error.message).toBe('Conflict');
|
||||
expect(error.name).toBe('ConflictError');
|
||||
});
|
||||
|
||||
it('should allow custom code for conflict scenarios', () => {
|
||||
const error = new ConflictError({code: 'USERNAME_TAKEN'});
|
||||
|
||||
expect(error.code).toBe('USERNAME_TAKEN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GoneError', () => {
|
||||
it('should have status 410 and default code/message', () => {
|
||||
const error = new GoneError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.GONE);
|
||||
expect(error.code).toBe('GONE');
|
||||
expect(error.message).toBe('Gone');
|
||||
expect(error.name).toBe('GoneError');
|
||||
});
|
||||
|
||||
it('should allow custom code for deleted resources', () => {
|
||||
const error = new GoneError({code: 'RESOURCE_DELETED'});
|
||||
|
||||
expect(error.code).toBe('RESOURCE_DELETED');
|
||||
});
|
||||
});
|
||||
|
||||
describe('InternalServerError', () => {
|
||||
it('should have status 500 and default code/message', () => {
|
||||
const error = new InternalServerError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(error.code).toBe('INTERNAL_SERVER_ERROR');
|
||||
expect(error.message).toBe('Internal Server Error');
|
||||
expect(error.name).toBe('InternalServerError');
|
||||
});
|
||||
|
||||
it('should allow custom code', () => {
|
||||
const error = new InternalServerError({code: 'DATABASE_ERROR'});
|
||||
|
||||
expect(error.code).toBe('DATABASE_ERROR');
|
||||
});
|
||||
|
||||
it('should preserve cause for debugging', () => {
|
||||
const cause = new Error('Database connection failed');
|
||||
const error = new InternalServerError({cause});
|
||||
|
||||
expect(error.cause).toBe(cause);
|
||||
});
|
||||
});
|
||||
|
||||
describe('NotImplementedError', () => {
|
||||
it('should have status 501 and default code/message', () => {
|
||||
const error = new NotImplementedError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.NOT_IMPLEMENTED);
|
||||
expect(error.code).toBe('NOT_IMPLEMENTED');
|
||||
expect(error.message).toBe('Not Implemented');
|
||||
expect(error.name).toBe('NotImplementedError');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ServiceUnavailableError', () => {
|
||||
it('should have status 503 and default code/message', () => {
|
||||
const error = new ServiceUnavailableError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
expect(error.code).toBe('SERVICE_UNAVAILABLE');
|
||||
expect(error.message).toBe('Service Unavailable');
|
||||
expect(error.name).toBe('ServiceUnavailableError');
|
||||
});
|
||||
|
||||
it('should include Retry-After header', () => {
|
||||
const error = new ServiceUnavailableError({
|
||||
headers: {'Retry-After': '300'},
|
||||
});
|
||||
|
||||
expect(error.headers).toEqual({'Retry-After': '300'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('BadGatewayError', () => {
|
||||
it('should have status 502 and default code/message', () => {
|
||||
const error = new BadGatewayError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.BAD_GATEWAY);
|
||||
expect(error.code).toBe('BAD_GATEWAY');
|
||||
expect(error.message).toBe('Bad Gateway');
|
||||
expect(error.name).toBe('BadGatewayError');
|
||||
});
|
||||
});
|
||||
|
||||
describe('GatewayTimeoutError', () => {
|
||||
it('should have status 504 and default code/message', () => {
|
||||
const error = new GatewayTimeoutError();
|
||||
|
||||
expect(error.status).toBe(HttpStatus.GATEWAY_TIMEOUT);
|
||||
expect(error.code).toBe('GATEWAY_TIMEOUT');
|
||||
expect(error.message).toBe('Gateway Timeout');
|
||||
expect(error.name).toBe('GatewayTimeoutError');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error inheritance', () => {
|
||||
it('all HTTP errors should extend FluxerError', () => {
|
||||
const errors = [
|
||||
new BadRequestError(),
|
||||
new UnauthorizedError(),
|
||||
new ForbiddenError(),
|
||||
new NotFoundError(),
|
||||
new MethodNotAllowedError(),
|
||||
new ConflictError(),
|
||||
new GoneError(),
|
||||
new InternalServerError(),
|
||||
new NotImplementedError(),
|
||||
new ServiceUnavailableError(),
|
||||
new BadGatewayError(),
|
||||
new GatewayTimeoutError(),
|
||||
];
|
||||
|
||||
for (const error of errors) {
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
expect(error).toBeInstanceOf(Error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('response generation', () => {
|
||||
it('should generate correct response for each error type', async () => {
|
||||
const testCases = [
|
||||
{error: new BadRequestError(), expectedStatus: 400},
|
||||
{error: new UnauthorizedError(), expectedStatus: 401},
|
||||
{error: new ForbiddenError(), expectedStatus: 403},
|
||||
{error: new NotFoundError(), expectedStatus: 404},
|
||||
{error: new MethodNotAllowedError(), expectedStatus: 405},
|
||||
{error: new ConflictError(), expectedStatus: 409},
|
||||
{error: new GoneError(), expectedStatus: 410},
|
||||
{error: new InternalServerError(), expectedStatus: 500},
|
||||
{error: new NotImplementedError(), expectedStatus: 501},
|
||||
{error: new BadGatewayError(), expectedStatus: 502},
|
||||
{error: new ServiceUnavailableError(), expectedStatus: 503},
|
||||
{error: new GatewayTimeoutError(), expectedStatus: 504},
|
||||
];
|
||||
|
||||
for (const {error, expectedStatus} of testCases) {
|
||||
const response = error.getResponse();
|
||||
expect(response.status).toBe(expectedStatus);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toHaveProperty('code');
|
||||
expect(body).toHaveProperty('message');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
297
packages/errors/src/__tests__/RateLimitError.test.tsx
Normal file
297
packages/errors/src/__tests__/RateLimitError.test.tsx
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {RateLimitError} from '@fluxer/errors/src/domains/core/RateLimitError';
|
||||
import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||
|
||||
interface RateLimitResponseBody {
|
||||
code: string;
|
||||
retry_after: number;
|
||||
global: boolean;
|
||||
}
|
||||
|
||||
describe('RateLimitError', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date('2026-01-27T12:00:00.000Z'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('valid inputs', () => {
|
||||
it('should create error with valid values', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.code).toBe('RATE_LIMITED');
|
||||
expect(error.status).toBe(429);
|
||||
expect(error.data?.retry_after).toBe(5);
|
||||
expect(error.headers?.['Retry-After']).toBe('5');
|
||||
expect(error.headers?.['X-RateLimit-Limit']).toBe('10');
|
||||
});
|
||||
|
||||
it('should use retryAfterDecimal when provided', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
retryAfterDecimal: 4.5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.data?.retry_after).toBe(4.5);
|
||||
expect(error.headers?.['Retry-After']).toBe('5');
|
||||
});
|
||||
|
||||
it('should set global flag correctly', () => {
|
||||
const globalError = new RateLimitError({
|
||||
global: true,
|
||||
retryAfter: 1,
|
||||
limit: 50,
|
||||
resetTime: new Date(Date.now() + 1000),
|
||||
});
|
||||
|
||||
expect(globalError.data?.global).toBe(true);
|
||||
expect(globalError.headers?.['X-RateLimit-Global']).toBe('true');
|
||||
|
||||
const bucketError = new RateLimitError({
|
||||
global: false,
|
||||
retryAfter: 1,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 1000),
|
||||
});
|
||||
|
||||
expect(bucketError.data?.global).toBe(false);
|
||||
expect(bucketError.headers?.['X-RateLimit-Global']).toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitization of invalid inputs', () => {
|
||||
it('should handle undefined retryAfter', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: undefined as unknown as number,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
expect(Number.isNaN(Number(error.headers?.['Retry-After']))).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle NaN retryAfter', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: NaN,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
expect(error.data?.retry_after).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle Infinity retryAfter', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: Infinity,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
});
|
||||
|
||||
it('should handle negative retryAfter', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: -5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
});
|
||||
|
||||
it('should handle zero retryAfter', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 0,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
});
|
||||
|
||||
it('should handle invalid resetTime', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 10,
|
||||
resetTime: new Date(NaN),
|
||||
});
|
||||
|
||||
const resetTimestamp = Number(error.headers?.['X-RateLimit-Reset']);
|
||||
const nowTimestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
expect(Number.isFinite(resetTimestamp)).toBe(true);
|
||||
expect(resetTimestamp).toBeGreaterThan(nowTimestamp);
|
||||
});
|
||||
|
||||
it('should handle resetTime in the past', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() - 10000),
|
||||
});
|
||||
|
||||
const resetTimestamp = Number(error.headers?.['X-RateLimit-Reset']);
|
||||
const nowTimestamp = Math.floor(Date.now() / 1000);
|
||||
|
||||
expect(resetTimestamp).toBeGreaterThan(nowTimestamp);
|
||||
});
|
||||
|
||||
it('should handle invalid limit', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: NaN,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['X-RateLimit-Limit']).toBe('1');
|
||||
});
|
||||
|
||||
it('should handle zero limit', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 0,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['X-RateLimit-Limit']).toBe('1');
|
||||
});
|
||||
|
||||
it('should handle NaN retryAfterDecimal', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
retryAfterDecimal: NaN,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
expect(error.data?.retry_after).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('response format', () => {
|
||||
it('should produce valid JSON response', async () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 10,
|
||||
limit: 50,
|
||||
resetTime: new Date(Date.now() + 10000),
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = (await response.json()) as RateLimitResponseBody;
|
||||
|
||||
expect(body.code).toBe('RATE_LIMITED');
|
||||
expect(body.retry_after).toBe(10);
|
||||
expect(body.global).toBe(false);
|
||||
expect(typeof body.retry_after).toBe('number');
|
||||
});
|
||||
|
||||
it('should never produce null retry_after in response', async () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: undefined as unknown as number,
|
||||
retryAfterDecimal: undefined,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = (await response.json()) as RateLimitResponseBody;
|
||||
|
||||
expect(body.retry_after).not.toBeNull();
|
||||
expect(typeof body.retry_after).toBe('number');
|
||||
expect(Number.isFinite(body.retry_after)).toBe(true);
|
||||
});
|
||||
|
||||
it('should set correct status code', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
expect(response.status).toBe(429);
|
||||
});
|
||||
|
||||
it('should include all required headers', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 5,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 5000),
|
||||
global: true,
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
expect(response.headers.get('Retry-After')).toBe('5');
|
||||
expect(response.headers.get('X-RateLimit-Global')).toBe('true');
|
||||
expect(response.headers.get('X-RateLimit-Limit')).toBe('10');
|
||||
expect(response.headers.get('X-RateLimit-Remaining')).toBe('0');
|
||||
expect(response.headers.get('X-RateLimit-Reset')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle all inputs being invalid simultaneously', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: NaN,
|
||||
retryAfterDecimal: Infinity,
|
||||
limit: -1,
|
||||
resetTime: new Date(NaN),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('1');
|
||||
expect(error.headers?.['X-RateLimit-Limit']).toBe('1');
|
||||
expect(Number.isFinite(Number(error.headers?.['X-RateLimit-Reset']))).toBe(true);
|
||||
expect(Number.isFinite(error.data?.retry_after as number)).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle very large retryAfter values', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 999999999,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 999999999000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('999999999');
|
||||
});
|
||||
|
||||
it('should ceil fractional retryAfter for header', () => {
|
||||
const error = new RateLimitError({
|
||||
retryAfter: 1.1,
|
||||
limit: 10,
|
||||
resetTime: new Date(Date.now() + 2000),
|
||||
});
|
||||
|
||||
expect(error.headers?.['Retry-After']).toBe('2');
|
||||
});
|
||||
});
|
||||
});
|
||||
227
packages/errors/src/__tests__/ValidationError.test.tsx
Normal file
227
packages/errors/src/__tests__/ValidationError.test.tsx
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HttpStatus} from '@fluxer/constants/src/HttpConstants';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
import {ValidationError} from '@fluxer/errors/src/ValidationError';
|
||||
import {describe, expect, it} from 'vitest';
|
||||
|
||||
interface ValidationErrorBody {
|
||||
code: string;
|
||||
message: string;
|
||||
errors: Array<{field: string; code: string; message: string}>;
|
||||
}
|
||||
|
||||
describe('ValidationError', () => {
|
||||
describe('constructor', () => {
|
||||
it('should create error with single field error', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [{field: 'email', code: 'INVALID_EMAIL', message: 'Invalid email format'}],
|
||||
});
|
||||
|
||||
expect(error.status).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(error.code).toBe('VALIDATION_ERROR');
|
||||
expect(error.message).toBe('Validation failed');
|
||||
expect(error.name).toBe('ValidationError');
|
||||
expect(error.errors).toEqual([{field: 'email', code: 'INVALID_EMAIL', message: 'Invalid email format'}]);
|
||||
});
|
||||
|
||||
it('should create error with multiple field errors', () => {
|
||||
const fieldErrors = [
|
||||
{field: 'email', code: 'REQUIRED', message: 'Email is required'},
|
||||
{field: 'password', code: 'TOO_SHORT', message: 'Password must be at least 8 characters'},
|
||||
{field: 'username', code: 'INVALID_CHARS', message: 'Username contains invalid characters'},
|
||||
];
|
||||
|
||||
const error = new ValidationError({errors: fieldErrors});
|
||||
|
||||
expect(error.errors).toHaveLength(3);
|
||||
expect(error.errors).toEqual(fieldErrors);
|
||||
});
|
||||
|
||||
it('should allow custom code', () => {
|
||||
const error = new ValidationError({
|
||||
code: 'INVALID_FORM_BODY',
|
||||
errors: [{field: 'name', code: 'REQUIRED', message: 'Name is required'}],
|
||||
});
|
||||
|
||||
expect(error.code).toBe('INVALID_FORM_BODY');
|
||||
});
|
||||
|
||||
it('should allow custom message', () => {
|
||||
const error = new ValidationError({
|
||||
message: 'Input validation failed',
|
||||
errors: [{field: 'name', code: 'REQUIRED', message: 'Name is required'}],
|
||||
});
|
||||
|
||||
expect(error.message).toBe('Input validation failed');
|
||||
});
|
||||
|
||||
it('should be instance of FluxerError', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [{field: 'field', code: 'CODE', message: 'message'}],
|
||||
});
|
||||
|
||||
expect(error).toBeInstanceOf(FluxerError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getResponse', () => {
|
||||
it('should return JSON response with errors array', async () => {
|
||||
const error = new ValidationError({
|
||||
errors: [{field: 'email', code: 'INVALID', message: 'Invalid email'}],
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(response.headers.get('Content-Type')).toBe('application/json');
|
||||
|
||||
const body = await response.json();
|
||||
expect(body).toEqual({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
errors: [{field: 'email', code: 'INVALID', message: 'Invalid email'}],
|
||||
});
|
||||
});
|
||||
|
||||
it('should include multiple errors in response', async () => {
|
||||
const fieldErrors = [
|
||||
{field: 'email', code: 'REQUIRED', message: 'Email is required'},
|
||||
{field: 'password', code: 'TOO_SHORT', message: 'Password too short'},
|
||||
];
|
||||
|
||||
const error = new ValidationError({errors: fieldErrors});
|
||||
const response = error.getResponse();
|
||||
const body = (await response.json()) as ValidationErrorBody;
|
||||
|
||||
expect(body.errors).toHaveLength(2);
|
||||
expect(body.errors).toEqual(fieldErrors);
|
||||
});
|
||||
|
||||
it('should include custom code and message in response', async () => {
|
||||
const error = new ValidationError({
|
||||
code: 'CUSTOM_VALIDATION',
|
||||
message: 'Custom validation message',
|
||||
errors: [{field: 'field', code: 'CODE', message: 'message'}],
|
||||
});
|
||||
|
||||
const response = error.getResponse();
|
||||
const body = (await response.json()) as ValidationErrorBody;
|
||||
|
||||
expect(body.code).toBe('CUSTOM_VALIDATION');
|
||||
expect(body.message).toBe('Custom validation message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromField static method', () => {
|
||||
it('should create ValidationError from single field', () => {
|
||||
const error = ValidationError.fromField('username', 'TAKEN', 'Username is already taken');
|
||||
|
||||
expect(error).toBeInstanceOf(ValidationError);
|
||||
expect(error.errors).toEqual([{field: 'username', code: 'TAKEN', message: 'Username is already taken'}]);
|
||||
});
|
||||
|
||||
it('should have default code and message', () => {
|
||||
const error = ValidationError.fromField('field', 'code', 'message');
|
||||
|
||||
expect(error.code).toBe('VALIDATION_ERROR');
|
||||
expect(error.message).toBe('Validation failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromFields static method', () => {
|
||||
it('should create ValidationError from multiple fields', () => {
|
||||
const fieldErrors = [
|
||||
{field: 'email', code: 'REQUIRED', message: 'Email is required'},
|
||||
{field: 'password', code: 'WEAK', message: 'Password is too weak'},
|
||||
];
|
||||
|
||||
const error = ValidationError.fromFields(fieldErrors);
|
||||
|
||||
expect(error).toBeInstanceOf(ValidationError);
|
||||
expect(error.errors).toEqual(fieldErrors);
|
||||
});
|
||||
|
||||
it('should handle empty array', () => {
|
||||
const error = ValidationError.fromFields([]);
|
||||
|
||||
expect(error.errors).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error data structure', () => {
|
||||
it('should store errors in data property', () => {
|
||||
const fieldErrors = [{field: 'test', code: 'TEST', message: 'Test error'}];
|
||||
const error = new ValidationError({errors: fieldErrors});
|
||||
|
||||
expect(error.data).toEqual({errors: fieldErrors});
|
||||
});
|
||||
|
||||
it('should serialize correctly with toJSON', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [{field: 'field', code: 'CODE', message: 'message'}],
|
||||
});
|
||||
|
||||
const json = error.toJSON();
|
||||
|
||||
expect(json).toEqual({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'Validation failed',
|
||||
errors: [{field: 'field', code: 'CODE', message: 'message'}],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle field errors with special characters', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [{field: 'user.email', code: 'INVALID', message: "Email can't be empty"}],
|
||||
});
|
||||
|
||||
expect(error.errors[0].field).toBe('user.email');
|
||||
expect(error.errors[0].message).toBe("Email can't be empty");
|
||||
});
|
||||
|
||||
it('should handle nested field paths', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [
|
||||
{field: 'address.street', code: 'REQUIRED', message: 'Street is required'},
|
||||
{field: 'address.city', code: 'REQUIRED', message: 'City is required'},
|
||||
{field: 'address.zip', code: 'INVALID_FORMAT', message: 'Invalid ZIP code format'},
|
||||
],
|
||||
});
|
||||
|
||||
expect(error.errors).toHaveLength(3);
|
||||
expect(error.errors[0].field).toBe('address.street');
|
||||
});
|
||||
|
||||
it('should handle array index field paths', () => {
|
||||
const error = new ValidationError({
|
||||
errors: [
|
||||
{field: 'items[0].name', code: 'REQUIRED', message: 'Item name is required'},
|
||||
{field: 'items[1].quantity', code: 'MIN', message: 'Quantity must be at least 1'},
|
||||
],
|
||||
});
|
||||
|
||||
expect(error.errors).toHaveLength(2);
|
||||
expect(error.errors[0].field).toBe('items[0].name');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class AdminApiKeyNotFoundError extends NotFoundError {
|
||||
constructor(messageVariables?: Record<string, unknown>) {
|
||||
super({
|
||||
code: APIErrorCodes.ADMIN_API_KEY_NOT_FOUND,
|
||||
messageVariables,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class NotOwnerOfAdminApiKeyError extends ForbiddenError {
|
||||
constructor(messageVariables?: Record<string, unknown>) {
|
||||
super({
|
||||
code: APIErrorCodes.NOT_OWNER_OF_ADMIN_API_KEY,
|
||||
messageVariables,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class BotUserAuthEndpointAccessDeniedError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.BOT_USER_AUTH_ENDPOINT_ACCESS_DENIED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class BotUserAuthSessionCreationDeniedError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.BOT_USER_AUTH_SESSION_CREATION_DENIED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {InternalServerError} from '@fluxer/errors/src/domains/core/InternalServerError';
|
||||
|
||||
export class EmailServiceNotTestableError extends InternalServerError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.EMAIL_SERVICE_NOT_TESTABLE});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class EmailVerificationRequiredError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.EMAIL_VERIFICATION_REQUIRED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class GuildPhoneVerificationRequiredError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.GUILD_PHONE_VERIFICATION_REQUIRED,
|
||||
});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/auth/HandoffCodeExpiredError.tsx
Normal file
27
packages/errors/src/domains/auth/HandoffCodeExpiredError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class HandoffCodeExpiredError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.HANDOFF_CODE_EXPIRED});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {OAuth2Error} from '@fluxer/errors/src/domains/auth/OAuth2Error';
|
||||
|
||||
export class HttpGetAuthorizeNotSupportedError extends OAuth2Error {
|
||||
constructor(message = 'GET /oauth2/authorize is not supported. Use POST /oauth2/authorize/consent.') {
|
||||
super({error: 'invalid_request', errorDescription: message, status: 400});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {UnauthorizedError} from '@fluxer/errors/src/domains/core/UnauthorizedError';
|
||||
|
||||
export class InvalidGatewayAuthTokenError extends UnauthorizedError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_AUTH_TOKEN});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/auth/InvalidHandoffCodeError.tsx
Normal file
27
packages/errors/src/domains/auth/InvalidHandoffCodeError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidHandoffCodeError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_HANDOFF_CODE});
|
||||
}
|
||||
}
|
||||
29
packages/errors/src/domains/auth/InvalidPhoneNumberError.tsx
Normal file
29
packages/errors/src/domains/auth/InvalidPhoneNumberError.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidPhoneNumberError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.INVALID_PHONE_NUMBER,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidPhoneVerificationCodeError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.INVALID_PHONE_VERIFICATION_CODE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {InternalServerError} from '@fluxer/errors/src/domains/core/InternalServerError';
|
||||
|
||||
export class InvalidWebAuthnAuthenticationCounterError extends InternalServerError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_WEBAUTHN_AUTHENTICATION_COUNTER});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidWebAuthnCredentialCounterError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_WEBAUTHN_CREDENTIAL_COUNTER});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidWebAuthnCredentialError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_WEBAUTHN_CREDENTIAL});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidWebAuthnPublicKeyFormatError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_WEBAUTHN_PUBLIC_KEY_FORMAT});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class IpAuthorizationRequiredError extends ForbiddenError {
|
||||
constructor({
|
||||
ticket,
|
||||
email,
|
||||
resendAvailableIn,
|
||||
}: {
|
||||
ticket: string;
|
||||
email: string;
|
||||
resendAvailableIn: number;
|
||||
}) {
|
||||
super({
|
||||
code: APIErrorCodes.IP_AUTHORIZATION_REQUIRED,
|
||||
data: {
|
||||
ip_authorization_required: true,
|
||||
ticket,
|
||||
email,
|
||||
resend_available_in: resendAvailableIn,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ThrottledError} from '@fluxer/errors/src/domains/core/ThrottledError';
|
||||
|
||||
export class IpAuthorizationResendCooldownError extends ThrottledError {
|
||||
constructor(resendAvailableIn: number) {
|
||||
super({
|
||||
code: APIErrorCodes.IP_AUTHORIZATION_RESEND_COOLDOWN,
|
||||
data: {resend_available_in: resendAvailableIn},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ThrottledError} from '@fluxer/errors/src/domains/core/ThrottledError';
|
||||
|
||||
export class IpAuthorizationResendLimitExceededError extends ThrottledError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.IP_AUTHORIZATION_RESEND_LIMIT_EXCEEDED});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/auth/MfaNotDisabledError.tsx
Normal file
27
packages/errors/src/domains/auth/MfaNotDisabledError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MfaNotDisabledError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.TWO_FA_NOT_ENABLED});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/auth/MfaNotEnabledError.tsx
Normal file
27
packages/errors/src/domains/auth/MfaNotEnabledError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MfaNotEnabledError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.TWO_FACTOR_REQUIRED});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {UnauthorizedError} from '@fluxer/errors/src/domains/core/UnauthorizedError';
|
||||
|
||||
export class MissingGatewayAuthorizationError extends UnauthorizedError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.MISSING_AUTHORIZATION});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/auth/MissingOAuthFieldsError.tsx
Normal file
27
packages/errors/src/domains/auth/MissingOAuthFieldsError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MissingOAuthFieldsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.MISSING_OAUTH_FIELDS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class NoPasskeysRegisteredError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.NO_PASSKEYS_REGISTERED});
|
||||
}
|
||||
}
|
||||
59
packages/errors/src/domains/auth/OAuth2Error.tsx
Normal file
59
packages/errors/src/domains/auth/OAuth2Error.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {HTTPException} from 'hono/http-exception';
|
||||
|
||||
type ErrorStatusCode = 400 | 401 | 403;
|
||||
|
||||
export class OAuth2Error extends HTTPException {
|
||||
error: string;
|
||||
errorDescription: string;
|
||||
override status: ErrorStatusCode;
|
||||
|
||||
constructor({
|
||||
error,
|
||||
errorDescription,
|
||||
status = 400,
|
||||
}: {
|
||||
error: string;
|
||||
errorDescription: string;
|
||||
status?: ErrorStatusCode;
|
||||
}) {
|
||||
super(status, {message: errorDescription});
|
||||
this.error = error;
|
||||
this.errorDescription = errorDescription;
|
||||
this.status = status;
|
||||
this.name = 'OAuth2Error';
|
||||
}
|
||||
|
||||
override getResponse(): Response {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: this.error,
|
||||
error_description: this.errorDescription,
|
||||
}),
|
||||
{
|
||||
status: this.status,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {UnauthorizedError} from '@fluxer/errors/src/domains/core/UnauthorizedError';
|
||||
|
||||
export class PasskeyAuthenticationFailedError extends UnauthorizedError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.PASSKEY_AUTHENTICATION_FAILED});
|
||||
}
|
||||
}
|
||||
29
packages/errors/src/domains/auth/PhoneAlreadyUsedError.tsx
Normal file
29
packages/errors/src/domains/auth/PhoneAlreadyUsedError.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class PhoneAlreadyUsedError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.PHONE_ALREADY_USED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class PhoneRequiredForSmsMfaError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.PHONE_REQUIRED_FOR_SMS_MFA,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class PhoneVerificationRequiredError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.PHONE_VERIFICATION_REQUIRED,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class SessionTokenMismatchError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.SESSION_TOKEN_MISMATCH});
|
||||
}
|
||||
}
|
||||
29
packages/errors/src/domains/auth/SmsMfaNotEnabledError.tsx
Normal file
29
packages/errors/src/domains/auth/SmsMfaNotEnabledError.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class SmsMfaNotEnabledError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.SMS_MFA_NOT_ENABLED,
|
||||
});
|
||||
}
|
||||
}
|
||||
29
packages/errors/src/domains/auth/SmsMfaRequiresTotpError.tsx
Normal file
29
packages/errors/src/domains/auth/SmsMfaRequiresTotpError.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class SmsMfaRequiresTotpError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.SMS_MFA_REQUIRES_TOTP,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class SmsVerificationUnavailableError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.SMS_VERIFICATION_UNAVAILABLE});
|
||||
}
|
||||
}
|
||||
28
packages/errors/src/domains/auth/SsoRequiredError.tsx
Normal file
28
packages/errors/src/domains/auth/SsoRequiredError.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class SsoRequiredError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.SSO_REQUIRED});
|
||||
this.name = 'SsoRequiredError';
|
||||
}
|
||||
}
|
||||
32
packages/errors/src/domains/auth/SudoModeRequiredError.tsx
Normal file
32
packages/errors/src/domains/auth/SudoModeRequiredError.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class SudoModeRequiredError extends ForbiddenError {
|
||||
constructor(hasMfa: boolean) {
|
||||
super({
|
||||
code: APIErrorCodes.SUDO_MODE_REQUIRED,
|
||||
data: {
|
||||
has_mfa: hasMfa,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class UnknownWebAuthnCredentialError extends NotFoundError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNKNOWN_WEBAUTHN_CREDENTIAL});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class WebAuthnCredentialLimitReachedError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.WEBAUTHN_CREDENTIAL_LIMIT_REACHED});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CallAlreadyExistsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CALL_ALREADY_EXISTS,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class CannotEditOtherUserMessageError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CANNOT_EDIT_OTHER_USER_MESSAGE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotEditSystemMessageError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CANNOT_MODIFY_SYSTEM_WEBHOOK});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotReportOwnMessageError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CANNOT_REPORT_OWN_MESSAGE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotSendEmptyMessageError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CANNOT_SEND_EMPTY_MESSAGE});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotSendMessageToNonTextChannelError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CANNOT_SEND_MESSAGES_IN_NON_TEXT_CHANNEL,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotSendMessagesToUserError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CANNOT_SEND_MESSAGES_TO_USER});
|
||||
}
|
||||
}
|
||||
30
packages/errors/src/domains/channel/ChannelIndexingError.tsx
Normal file
30
packages/errors/src/domains/channel/ChannelIndexingError.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class ChannelIndexingError extends FluxerError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CHANNEL_INDEXING,
|
||||
status: 202,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class EmptyStreamThumbnailPayloadError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.STREAM_THUMBNAIL_PAYLOAD_EMPTY});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidChannelTypeError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.INVALID_CHANNEL_TYPE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidChannelTypeForCallError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.INVALID_CHANNEL_TYPE_FOR_CALL,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidStreamKeyFormatError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_STREAM_KEY_FORMAT});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class InvalidStreamThumbnailPayloadError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.INVALID_STREAM_THUMBNAIL_PAYLOAD});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxCategoryChannelsError extends BadRequestError {
|
||||
constructor(maxChannels: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_CATEGORY_CHANNELS,
|
||||
messageVariables: {maxChannels},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxGroupDmRecipientsError extends BadRequestError {
|
||||
constructor(limit: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_GROUP_DM_RECIPIENTS,
|
||||
messageVariables: {limit},
|
||||
data: {
|
||||
max_recipients: limit,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
33
packages/errors/src/domains/channel/MaxGroupDmsError.tsx
Normal file
33
packages/errors/src/domains/channel/MaxGroupDmsError.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxGroupDmsError extends BadRequestError {
|
||||
constructor(limit: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_GROUP_DMS,
|
||||
messageVariables: {limit},
|
||||
data: {
|
||||
max_group_dms: limit,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxReactionsPerMessageError extends BadRequestError {
|
||||
constructor(limit: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_REACTIONS,
|
||||
messageVariables: {limit},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxUsersPerMessageReactionError extends BadRequestError {
|
||||
constructor(limit: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_REACTIONS,
|
||||
messageVariables: {limit},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MaxWebhooksPerChannelError extends BadRequestError {
|
||||
constructor(limit: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MAX_WEBHOOKS_PER_CHANNEL,
|
||||
messageVariables: {limit},
|
||||
data: {
|
||||
max_webhooks_per_channel: limit,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class MessageTotalSizeTooLargeError extends BadRequestError {
|
||||
constructor(maxSize: number) {
|
||||
super({
|
||||
code: APIErrorCodes.MESSAGE_TOTAL_ATTACHMENT_SIZE_TOO_LARGE,
|
||||
messageVariables: {maxSize},
|
||||
});
|
||||
}
|
||||
}
|
||||
29
packages/errors/src/domains/channel/NoActiveCallError.tsx
Normal file
29
packages/errors/src/domains/channel/NoActiveCallError.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class NoActiveCallError extends NotFoundError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.NO_ACTIVE_CALL,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class StreamKeyChannelMismatchError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.STREAM_KEY_CHANNEL_MISMATCH});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotAddReactionsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_ADD_REACTIONS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotJoinGroupDmsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_JOIN_GROUP_DMS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotJoinOneOnOneVoiceCallsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_JOIN_ONE_ON_ONE_VOICE_CALLS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotJoinVoiceChannelsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_JOIN_VOICE_CHANNELS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotSendDirectMessagesError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_SEND_DIRECT_MESSAGES});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class UnclaimedAccountCannotSendMessagesError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNCLAIMED_ACCOUNT_CANNOT_SEND_MESSAGES});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/channel/UnknownChannelError.tsx
Normal file
27
packages/errors/src/domains/channel/UnknownChannelError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class UnknownChannelError extends NotFoundError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNKNOWN_CHANNEL});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/channel/UnknownMessageError.tsx
Normal file
27
packages/errors/src/domains/channel/UnknownMessageError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class UnknownMessageError extends NotFoundError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.UNKNOWN_MESSAGE});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class ConnectionAlreadyExistsError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_ALREADY_EXISTS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class ConnectionInvalidIdentifierError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_INVALID_IDENTIFIER});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class ConnectionInvalidTypeError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_INVALID_TYPE});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class ConnectionLimitReachedError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_LIMIT_REACHED});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {NotFoundError} from '@fluxer/errors/src/domains/core/NotFoundError';
|
||||
|
||||
export class ConnectionNotFoundError extends NotFoundError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_NOT_FOUND});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class ConnectionVerificationFailedError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONNECTION_VERIFICATION_FAILED});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/content/ContentBlockedError.tsx
Normal file
27
packages/errors/src/domains/content/ContentBlockedError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class ContentBlockedError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CONTENT_BLOCKED});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/core/AccessDeniedError.tsx
Normal file
27
packages/errors/src/domains/core/AccessDeniedError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class AccessDeniedError extends FluxerError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.ACCESS_DENIED, status: 403});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/core/AclsMustBeNonEmptyError.tsx
Normal file
27
packages/errors/src/domains/core/AclsMustBeNonEmptyError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class AclsMustBeNonEmptyError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.ACLS_MUST_BE_NON_EMPTY});
|
||||
}
|
||||
}
|
||||
30
packages/errors/src/domains/core/AuditLogIndexingError.tsx
Normal file
30
packages/errors/src/domains/core/AuditLogIndexingError.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class AuditLogIndexingError extends FluxerError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.AUDIT_LOG_INDEXING,
|
||||
status: 202,
|
||||
});
|
||||
}
|
||||
}
|
||||
40
packages/errors/src/domains/core/BadGatewayError.tsx
Normal file
40
packages/errors/src/domains/core/BadGatewayError.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {APIErrorCode} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError, type FluxerErrorData} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class BadGatewayError extends FluxerError {
|
||||
constructor({
|
||||
code = APIErrorCodes.BAD_GATEWAY,
|
||||
message,
|
||||
data,
|
||||
headers,
|
||||
messageVariables,
|
||||
}: {
|
||||
code?: APIErrorCode;
|
||||
message?: string;
|
||||
data?: FluxerErrorData;
|
||||
headers?: Record<string, string>;
|
||||
messageVariables?: Record<string, unknown>;
|
||||
} = {}) {
|
||||
super({code, message, status: 502, data, headers, messageVariables});
|
||||
}
|
||||
}
|
||||
39
packages/errors/src/domains/core/BadRequestError.tsx
Normal file
39
packages/errors/src/domains/core/BadRequestError.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {APIErrorCode} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError, type FluxerErrorData} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class BadRequestError extends FluxerError {
|
||||
constructor({
|
||||
code,
|
||||
message,
|
||||
headers,
|
||||
data,
|
||||
messageVariables,
|
||||
}: {
|
||||
code: APIErrorCode;
|
||||
message?: string;
|
||||
data?: FluxerErrorData;
|
||||
headers?: Record<string, string>;
|
||||
messageVariables?: Record<string, unknown>;
|
||||
}) {
|
||||
super({code, message, status: 400, data, headers, messageVariables});
|
||||
}
|
||||
}
|
||||
27
packages/errors/src/domains/core/CannotExecuteOnDmError.tsx
Normal file
27
packages/errors/src/domains/core/CannotExecuteOnDmError.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotExecuteOnDmError extends BadRequestError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.CANNOT_EXECUTE_ON_DM});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
|
||||
|
||||
export class CannotRemoveOtherRecipientsError extends ForbiddenError {
|
||||
constructor() {
|
||||
super({code: APIErrorCodes.MISSING_PERMISSIONS});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CannotShrinkReservedSlotsError extends BadRequestError {
|
||||
constructor(reservedSlotIndices: Array<number>) {
|
||||
super({
|
||||
code: APIErrorCodes.CANNOT_SHRINK_RESERVED_SLOTS,
|
||||
messageVariables: {
|
||||
count: reservedSlotIndices.length,
|
||||
indices: reservedSlotIndices.join(', '),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
|
||||
|
||||
export class CaptchaVerificationRequiredError extends BadRequestError {
|
||||
constructor() {
|
||||
super({
|
||||
code: APIErrorCodes.CAPTCHA_REQUIRED,
|
||||
});
|
||||
}
|
||||
}
|
||||
39
packages/errors/src/domains/core/ConflictError.tsx
Normal file
39
packages/errors/src/domains/core/ConflictError.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {APIErrorCode} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {FluxerError, type FluxerErrorData} from '@fluxer/errors/src/FluxerError';
|
||||
|
||||
export class ConflictError extends FluxerError {
|
||||
constructor({
|
||||
code,
|
||||
message,
|
||||
data,
|
||||
headers,
|
||||
messageVariables,
|
||||
}: {
|
||||
code: APIErrorCode;
|
||||
message?: string;
|
||||
data?: FluxerErrorData;
|
||||
headers?: Record<string, string>;
|
||||
messageVariables?: Record<string, unknown>;
|
||||
}) {
|
||||
super({code, message, status: 409, data, headers, messageVariables});
|
||||
}
|
||||
}
|
||||
30
packages/errors/src/domains/core/CreationFailedError.tsx
Normal file
30
packages/errors/src/domains/core/CreationFailedError.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
import {InternalServerError} from '@fluxer/errors/src/domains/core/InternalServerError';
|
||||
|
||||
export class CreationFailedError extends InternalServerError {
|
||||
constructor(detail?: string) {
|
||||
super({
|
||||
code: APIErrorCodes.CREATION_FAILED,
|
||||
messageVariables: detail ? {detail} : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user