diff --git a/src/http/__test__/errors.test.ts b/src/http/__test__/errors.test.ts index a10a7ad..609201e 100644 --- a/src/http/__test__/errors.test.ts +++ b/src/http/__test__/errors.test.ts @@ -32,4 +32,24 @@ describe('ResponseError', () => { expect(error.message).toBe('Hello!'); expect(error.statusCode).toBe(500); }); + + it('should handle init', () => { + const error = new ResponseError('Need redirect', { + statusCode: 301, + redirectLocation: '/hello.php', + logLevel: 'debug', + }); + + expect(error.logLevel).toBe('debug'); + expect(error.statusCode).toBe(301); + expect(error.redirectLocation).toBe('/hello.php'); + }); + + it('should handle empty init', () => { + const error = new ResponseError('Need redirect', {}); + + expect(error.logLevel).toBe('error'); + expect(error.statusCode).toBe(500); + expect(error.redirectLocation).toBe(null); + }); }); diff --git a/src/http/errors.ts b/src/http/errors.ts index 9277272..d7dbda7 100644 --- a/src/http/errors.ts +++ b/src/http/errors.ts @@ -1,3 +1,6 @@ +import type { LogLevel } from '../log'; +import type { ResponseErrorInit } from './types'; + /** * Ошибка валидации статуса ответа. * @todo Переименовать в HttpStatusValidationError? @@ -28,15 +31,26 @@ export class StatusError extends Error { * Ошибка в процессе формирования ответа. */ export class ResponseError extends Error { + logLevel: LogLevel | null; statusCode: number; + redirectLocation: string | null; /** * @param message Сообщение. - * @param statusCode Код ответа. + * @param statusCodeOrInit Код ответа. */ - constructor(message: string, statusCode = 500) { + constructor(message: string, statusCodeOrInit: number | ResponseErrorInit = 500) { super(message); this.name = 'ResponseError'; - this.statusCode = statusCode; + + if (typeof statusCodeOrInit === 'number') { + this.logLevel = 'error'; + this.statusCode = statusCodeOrInit; + this.redirectLocation = null; + } else { + this.logLevel = statusCodeOrInit.logLevel ?? 'error'; + this.statusCode = statusCodeOrInit.statusCode ?? 500; + this.redirectLocation = statusCodeOrInit.redirectLocation ?? null; + } } } diff --git a/src/http/index.ts b/src/http/index.ts index 42acc55..4303d02 100644 --- a/src/http/index.ts +++ b/src/http/index.ts @@ -11,6 +11,7 @@ export type { EitherResponse, ResponseDone, ResponseFail, + ResponseErrorInit, } from './types'; export { configureFetch, applyMiddleware, createCookieStore } from '@krutoo/fetch-tools'; export { log, cookie, defaultHeaders, validateStatus } from '@krutoo/fetch-tools/middleware'; diff --git a/src/http/types.ts b/src/http/types.ts index 702d8a5..3f4e633 100644 --- a/src/http/types.ts +++ b/src/http/types.ts @@ -1,3 +1,5 @@ +import { LogLevel } from '../log'; + export type { Handler, Enhancer, Middleware, CookieStore } from '@krutoo/fetch-tools'; export type { LogData, @@ -26,3 +28,9 @@ export interface ResponseFail { } export type EitherResponse = ResponseDone | ResponseFail; + +export interface ResponseErrorInit { + statusCode?: number; + redirectLocation?: string; + logLevel?: LogLevel | null; +} diff --git a/src/preset/bun/utils/get-stats-handler.ts b/src/preset/bun/utils/get-stats-handler.ts index 01acaf1..cb6141f 100644 --- a/src/preset/bun/utils/get-stats-handler.ts +++ b/src/preset/bun/utils/get-stats-handler.ts @@ -7,9 +7,10 @@ import type { Handler } from '../../../http'; export function getStatsHandler(): Handler { /** @inheritdoc */ const getHeapStats = async () => { + // ВАЖНО: должны быть именно кавычки "'" по причине https://github.com/web-infra-dev/rspack/issues/5938#issuecomment-2000393152 const jsc = await import( /* webpackIgnore: true */ - `bun:jsc` + 'bun:jsc' ); return jsc.heapStats(); diff --git a/src/preset/server/constants.ts b/src/preset/server/constants.ts index e485af9..87ded07 100644 --- a/src/preset/server/constants.ts +++ b/src/preset/server/constants.ts @@ -6,3 +6,9 @@ export const PAGE_HANDLER_EVENT_TYPE = { renderStart: 'isomorph/render:start', renderFinish: 'isomorph/render:finish', } as const; + +/** + * Приоритет форматов ответа на запрос страницы. + * Нужен для использования вместе с пакетом accepts. + */ +export const PAGE_FORMAT_PRIORITY = ['json', 'html']; diff --git a/src/tokens.ts b/src/tokens.ts index 4279ce4..df2c440 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -11,7 +11,7 @@ import type { BridgeClientSide, BridgeServerSide } from './utils/ssr'; import type { Tracer } from '@opentelemetry/api'; import type { BasicTracerProvider, SpanExporter } from '@opentelemetry/sdk-trace-base'; import type { Resource } from '@opentelemetry/resources'; -import type { ElementType, ReactNode } from 'react'; +import type { ElementType, ReactNode, JSX } from 'react'; import type { KnownHttpApiKey, PageAssets } from './preset/isomorphic/types'; import type { ExpressHandlerContext } from './preset/node/types'; import type { SpecificExtras } from './preset/server/utils/specific-extras'; @@ -19,7 +19,13 @@ import type { CreateAxiosDefaults } from 'axios'; import type { AxiosInstanceWrapper, Middleware as AxiosMiddleware } from 'middleware-axios'; import type { CookieStore, Handler, LogHandler, LogHandlerFactory, Middleware } from './http'; import type { HttpApiHostPool } from './preset/isomorphic/utils/http-api-host-pool'; -import type { ServerHandlerContext, ServerHandler, ServerMiddleware } from './preset/server/types'; +import type { + ServerHandlerContext, + ServerHandler, + ServerMiddleware, + PageResponseFormatter, + RenderToString, +} from './preset/server/types'; /** * Токены компонентов. @@ -114,12 +120,18 @@ export const KnownToken = { /** Токен компонентов входящего запроса. */ Request: { + /** Токен функции которая определяет возможные типы ответа и их приоритет. */ + acceptType: createToken<(types: string[]) => string | string[] | false>('handler/accepts'), + /** Токен "специфичных" параметров запроса. В зависимости от реализации определит параметры на основе объекта запроса. */ specificParams: createToken>('request/specific-params'), }, /** Токены компонентов исходящего ответа. */ Response: { + /** Токен объекта для подписки на события и вызова событий ответа. */ + events: createToken('response/events'), + /** Токен "специфичных" дополнительных данных. В зависимости от реализации сформирует дополнительные данные ответа. */ specificExtras: createToken('response/specific-extras'), }, @@ -134,6 +146,12 @@ export const KnownToken = { /** Токен "шлема". Шлем - UI-компонент, внутри которого будет выведен результат render-функции. */ helmet: createToken>('page/helmet'), + + /** Токен функции, получающей jsx и возвращающей строку. */ + elementToString: createToken('page/element-to-string'), + + /** Токен функции, которая вернёт данные для ответа. */ + formatResponse: createToken('page/format-response'), }, }, },