@tramvai/module-server 3.9.1 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/server/{error.d.ts → error/index.d.ts} +5 -3
- package/lib/server/error/index.es.js +93 -0
- package/lib/server/error/index.js +101 -0
- package/lib/server/error/prepareLogsForError.d.ts +11 -0
- package/lib/server/error/prepareLogsForError.es.js +63 -0
- package/lib/server/error/prepareLogsForError.js +67 -0
- package/lib/server/error/renderErrorBoundaryPageToString.d.ts +11 -0
- package/lib/server/error/renderErrorBoundaryPageToString.es.js +30 -0
- package/lib/server/error/renderErrorBoundaryPageToString.js +34 -0
- package/lib/server/error/serveRootErrorBoundary.d.ts +6 -0
- package/lib/server/error/serveRootErrorBoundary.es.js +13 -0
- package/lib/server/error/serveRootErrorBoundary.js +17 -0
- package/lib/server/error/utils.d.ts +12 -0
- package/lib/server/error/utils.es.js +32 -0
- package/lib/server/error/utils.js +38 -0
- package/lib/server/webApp.d.ts +3 -1
- package/lib/server/webApp.es.js +9 -3
- package/lib/server/webApp.js +9 -3
- package/lib/server.es.js +5 -1
- package/lib/server.js +4 -0
- package/package.json +15 -15
- package/lib/server/error.es.js +0 -163
- package/lib/server/error.js +0 -171
|
@@ -3,10 +3,12 @@ import type { LOGGER_TOKEN } from '@tramvai/module-common';
|
|
|
3
3
|
import type { FETCH_WEBPACK_STATS_TOKEN } from '@tramvai/tokens-render';
|
|
4
4
|
import type { WEB_FASTIFY_APP_AFTER_ERROR_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN } from '@tramvai/tokens-server-private';
|
|
5
5
|
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
6
|
-
|
|
6
|
+
import type { STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN } from '@tramvai/tokens-server';
|
|
7
|
+
export declare const errorHandler: (app: FastifyInstance, { log, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }: {
|
|
7
8
|
log: ReturnType<typeof LOGGER_TOKEN>;
|
|
8
9
|
beforeError: ExtractDependencyType<typeof WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN>;
|
|
9
10
|
afterError: ExtractDependencyType<typeof WEB_FASTIFY_APP_AFTER_ERROR_TOKEN>;
|
|
10
11
|
fetchWebpackStats: ExtractDependencyType<typeof FETCH_WEBPACK_STATS_TOKEN>;
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
staticRootErrorBoundaryError: ExtractDependencyType<typeof STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN>;
|
|
13
|
+
}) => Promise<void>;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import isNil from '@tinkoff/utils/is/nil';
|
|
2
|
+
import { isRedirectFoundError } from '@tinkoff/errors';
|
|
3
|
+
import { getRootErrorBoundary, getRequestInfo, runHandlersFactory } from './utils.es.js';
|
|
4
|
+
import { serveRootErrorBoundary } from './serveRootErrorBoundary.es.js';
|
|
5
|
+
import { prepareLogsForError } from './prepareLogsForError.es.js';
|
|
6
|
+
import { renderErrorBoundaryPageToString } from './renderErrorBoundaryPageToString.es.js';
|
|
7
|
+
|
|
8
|
+
const errorHandler = async (app, { log, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }) => {
|
|
9
|
+
const webpackStats = await fetchWebpackStats();
|
|
10
|
+
const RootErrorBoundary = getRootErrorBoundary();
|
|
11
|
+
const isRootErrorBoundaryExist = RootErrorBoundary !== null;
|
|
12
|
+
if (process.env.TRAMVAI_CLI_COMMAND === 'static' && isRootErrorBoundaryExist) {
|
|
13
|
+
serveRootErrorBoundary({
|
|
14
|
+
response: renderErrorBoundaryPageToString({
|
|
15
|
+
element: RootErrorBoundary,
|
|
16
|
+
requestUrl: '/5xx.html',
|
|
17
|
+
error: staticRootErrorBoundaryError !== null && staticRootErrorBoundaryError !== void 0 ? staticRootErrorBoundaryError : {
|
|
18
|
+
name: 'STATIC_ROOT_ERROR_BOUNDARY_ERROR',
|
|
19
|
+
message: 'Default error for root error boundary',
|
|
20
|
+
},
|
|
21
|
+
httpStatus: 500,
|
|
22
|
+
webpackStats,
|
|
23
|
+
}),
|
|
24
|
+
app,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
app.setErrorHandler(async (error, request, reply) => {
|
|
28
|
+
const runHandlers = runHandlersFactory(error, request, reply);
|
|
29
|
+
const requestInfo = getRequestInfo(request);
|
|
30
|
+
const beforeErrorResult = await runHandlers(beforeError);
|
|
31
|
+
if (!isNil(beforeErrorResult)) {
|
|
32
|
+
return beforeErrorResult;
|
|
33
|
+
}
|
|
34
|
+
if (isRedirectFoundError(error)) {
|
|
35
|
+
log.info({
|
|
36
|
+
event: 'redirect-found-error',
|
|
37
|
+
message: `RedirectFoundError, redirect to ${error.nextUrl}, action execution will be aborted.
|
|
38
|
+
More information about redirects - https://tramvai.dev/docs/features/routing/redirects`,
|
|
39
|
+
error,
|
|
40
|
+
requestInfo,
|
|
41
|
+
});
|
|
42
|
+
reply
|
|
43
|
+
.header('cache-control', 'no-store, no-cache, must-revalidate')
|
|
44
|
+
.redirect(error.httpStatus || 307, error.nextUrl);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const { logMessage, logLevel, logEvent, httpStatus } = prepareLogsForError({
|
|
48
|
+
error,
|
|
49
|
+
isRootErrorBoundaryExist,
|
|
50
|
+
});
|
|
51
|
+
log[logLevel]({
|
|
52
|
+
event: logEvent,
|
|
53
|
+
message: logMessage,
|
|
54
|
+
error,
|
|
55
|
+
requestInfo,
|
|
56
|
+
});
|
|
57
|
+
const afterErrorResult = await runHandlers(afterError);
|
|
58
|
+
if (!isNil(afterErrorResult)) {
|
|
59
|
+
return afterErrorResult;
|
|
60
|
+
}
|
|
61
|
+
reply.status(httpStatus);
|
|
62
|
+
if (isRootErrorBoundaryExist) {
|
|
63
|
+
try {
|
|
64
|
+
const response = renderErrorBoundaryPageToString({
|
|
65
|
+
element: RootErrorBoundary,
|
|
66
|
+
requestUrl: requestInfo.url,
|
|
67
|
+
webpackStats,
|
|
68
|
+
error,
|
|
69
|
+
httpStatus,
|
|
70
|
+
});
|
|
71
|
+
log.info({
|
|
72
|
+
event: 'render-root-error-boundary',
|
|
73
|
+
message: 'Render Root Error Boundary for the client',
|
|
74
|
+
});
|
|
75
|
+
reply
|
|
76
|
+
.header('Content-Type', 'text/html; charset=utf-8')
|
|
77
|
+
.header('Content-Length', Buffer.byteLength(response, 'utf8'))
|
|
78
|
+
.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
log.warn({
|
|
83
|
+
event: 'failed-root-error-boundary',
|
|
84
|
+
message: 'Root Error Boundary rendering failed',
|
|
85
|
+
error: e,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw error;
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export { errorHandler };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var isNil = require('@tinkoff/utils/is/nil');
|
|
6
|
+
var errors = require('@tinkoff/errors');
|
|
7
|
+
var utils = require('./utils.js');
|
|
8
|
+
var serveRootErrorBoundary = require('./serveRootErrorBoundary.js');
|
|
9
|
+
var prepareLogsForError = require('./prepareLogsForError.js');
|
|
10
|
+
var renderErrorBoundaryPageToString = require('./renderErrorBoundaryPageToString.js');
|
|
11
|
+
|
|
12
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
13
|
+
|
|
14
|
+
var isNil__default = /*#__PURE__*/_interopDefaultLegacy(isNil);
|
|
15
|
+
|
|
16
|
+
const errorHandler = async (app, { log, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }) => {
|
|
17
|
+
const webpackStats = await fetchWebpackStats();
|
|
18
|
+
const RootErrorBoundary = utils.getRootErrorBoundary();
|
|
19
|
+
const isRootErrorBoundaryExist = RootErrorBoundary !== null;
|
|
20
|
+
if (process.env.TRAMVAI_CLI_COMMAND === 'static' && isRootErrorBoundaryExist) {
|
|
21
|
+
serveRootErrorBoundary.serveRootErrorBoundary({
|
|
22
|
+
response: renderErrorBoundaryPageToString.renderErrorBoundaryPageToString({
|
|
23
|
+
element: RootErrorBoundary,
|
|
24
|
+
requestUrl: '/5xx.html',
|
|
25
|
+
error: staticRootErrorBoundaryError !== null && staticRootErrorBoundaryError !== void 0 ? staticRootErrorBoundaryError : {
|
|
26
|
+
name: 'STATIC_ROOT_ERROR_BOUNDARY_ERROR',
|
|
27
|
+
message: 'Default error for root error boundary',
|
|
28
|
+
},
|
|
29
|
+
httpStatus: 500,
|
|
30
|
+
webpackStats,
|
|
31
|
+
}),
|
|
32
|
+
app,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
app.setErrorHandler(async (error, request, reply) => {
|
|
36
|
+
const runHandlers = utils.runHandlersFactory(error, request, reply);
|
|
37
|
+
const requestInfo = utils.getRequestInfo(request);
|
|
38
|
+
const beforeErrorResult = await runHandlers(beforeError);
|
|
39
|
+
if (!isNil__default["default"](beforeErrorResult)) {
|
|
40
|
+
return beforeErrorResult;
|
|
41
|
+
}
|
|
42
|
+
if (errors.isRedirectFoundError(error)) {
|
|
43
|
+
log.info({
|
|
44
|
+
event: 'redirect-found-error',
|
|
45
|
+
message: `RedirectFoundError, redirect to ${error.nextUrl}, action execution will be aborted.
|
|
46
|
+
More information about redirects - https://tramvai.dev/docs/features/routing/redirects`,
|
|
47
|
+
error,
|
|
48
|
+
requestInfo,
|
|
49
|
+
});
|
|
50
|
+
reply
|
|
51
|
+
.header('cache-control', 'no-store, no-cache, must-revalidate')
|
|
52
|
+
.redirect(error.httpStatus || 307, error.nextUrl);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { logMessage, logLevel, logEvent, httpStatus } = prepareLogsForError.prepareLogsForError({
|
|
56
|
+
error,
|
|
57
|
+
isRootErrorBoundaryExist,
|
|
58
|
+
});
|
|
59
|
+
log[logLevel]({
|
|
60
|
+
event: logEvent,
|
|
61
|
+
message: logMessage,
|
|
62
|
+
error,
|
|
63
|
+
requestInfo,
|
|
64
|
+
});
|
|
65
|
+
const afterErrorResult = await runHandlers(afterError);
|
|
66
|
+
if (!isNil__default["default"](afterErrorResult)) {
|
|
67
|
+
return afterErrorResult;
|
|
68
|
+
}
|
|
69
|
+
reply.status(httpStatus);
|
|
70
|
+
if (isRootErrorBoundaryExist) {
|
|
71
|
+
try {
|
|
72
|
+
const response = renderErrorBoundaryPageToString.renderErrorBoundaryPageToString({
|
|
73
|
+
element: RootErrorBoundary,
|
|
74
|
+
requestUrl: requestInfo.url,
|
|
75
|
+
webpackStats,
|
|
76
|
+
error,
|
|
77
|
+
httpStatus,
|
|
78
|
+
});
|
|
79
|
+
log.info({
|
|
80
|
+
event: 'render-root-error-boundary',
|
|
81
|
+
message: 'Render Root Error Boundary for the client',
|
|
82
|
+
});
|
|
83
|
+
reply
|
|
84
|
+
.header('Content-Type', 'text/html; charset=utf-8')
|
|
85
|
+
.header('Content-Length', Buffer.byteLength(response, 'utf8'))
|
|
86
|
+
.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
87
|
+
return response;
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
log.warn({
|
|
91
|
+
event: 'failed-root-error-boundary',
|
|
92
|
+
message: 'Root Error Boundary rendering failed',
|
|
93
|
+
error: e,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
exports.errorHandler = errorHandler;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FastifyError } from 'fastify';
|
|
2
|
+
export declare const prepareLogsForError: ({ error, isRootErrorBoundaryExist, }: {
|
|
3
|
+
error: FastifyError;
|
|
4
|
+
isRootErrorBoundaryExist: boolean;
|
|
5
|
+
}) => {
|
|
6
|
+
logMessage: string;
|
|
7
|
+
logLevel: string;
|
|
8
|
+
logEvent: string;
|
|
9
|
+
httpStatus: number;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=prepareLogsForError.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isNotFoundError, isHttpError } from '@tinkoff/errors';
|
|
2
|
+
|
|
3
|
+
// eslint-disable-next-line max-statements
|
|
4
|
+
const prepareLogsForError = ({ error, isRootErrorBoundaryExist, }) => {
|
|
5
|
+
let httpStatus;
|
|
6
|
+
let logLevel;
|
|
7
|
+
let logEvent;
|
|
8
|
+
let logMessage;
|
|
9
|
+
if (isNotFoundError(error)) {
|
|
10
|
+
httpStatus = error.httpStatus || 404;
|
|
11
|
+
logLevel = 'info';
|
|
12
|
+
logEvent = 'not-found-error';
|
|
13
|
+
logMessage = `NotFoundError, action execution will be aborted.
|
|
14
|
+
Not Found page is common use-case with this error - https://tramvai.dev/docs/features/routing/wildcard-routes/#not-found-page`;
|
|
15
|
+
}
|
|
16
|
+
else if (isHttpError(error)) {
|
|
17
|
+
httpStatus = error.httpStatus || 500;
|
|
18
|
+
if (error.httpStatus >= 500) {
|
|
19
|
+
logLevel = 'error';
|
|
20
|
+
logEvent = 'send-server-error';
|
|
21
|
+
logMessage = `This is expected server error, here is most common cases:
|
|
22
|
+
- Router Guard blocked request - https://tramvai.dev/docs/features/routing/hooks-and-guards#guards
|
|
23
|
+
- Forced Page Error Boundary render with 5xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action`;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
logLevel = 'info';
|
|
27
|
+
logEvent = 'http-error';
|
|
28
|
+
logMessage = `This is expected server error, here is most common cases:
|
|
29
|
+
- Route is not found - https://tramvai.dev/docs/features/routing/flow#server-navigation
|
|
30
|
+
- Forced Page Error Boundary render with 4xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action
|
|
31
|
+
- Request Limiter blocked request with 429 code - https://tramvai.dev/docs/references/modules/request-limiter/`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
httpStatus = error.statusCode || 500;
|
|
36
|
+
if (error.statusCode >= 500) {
|
|
37
|
+
logLevel = 'error';
|
|
38
|
+
logEvent = 'send-server-error';
|
|
39
|
+
logMessage = `This is Fastify 5xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
40
|
+
}
|
|
41
|
+
else if (error.statusCode >= 400) {
|
|
42
|
+
// a lot of noise with FST_ERR_CTP_INVALID_MEDIA_TYPE 4xx logs from Fastify,
|
|
43
|
+
// when somebody tries to scan our site and send some unsupported content types
|
|
44
|
+
logLevel = 'info';
|
|
45
|
+
logEvent = 'fastify-error-4xx';
|
|
46
|
+
logMessage = `This is Fastify 4xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
logLevel = 'error';
|
|
50
|
+
logEvent = 'send-server-error';
|
|
51
|
+
logMessage = `Unexpected server error. Error cause will be in "error" parameter.
|
|
52
|
+
Most likely an error has occurred in the rendering of the current React page component
|
|
53
|
+
You can try to find relative logs by using "x-request-id" header`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
logMessage = `${logMessage}
|
|
57
|
+
${isRootErrorBoundaryExist
|
|
58
|
+
? 'Root Error Boundary will be rendered for the client'
|
|
59
|
+
: 'You can add Error Boundary for better UX - https://tramvai.dev/docs/features/error-boundaries'}'`;
|
|
60
|
+
return { logMessage, logLevel, logEvent, httpStatus };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { prepareLogsForError };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var errors = require('@tinkoff/errors');
|
|
6
|
+
|
|
7
|
+
// eslint-disable-next-line max-statements
|
|
8
|
+
const prepareLogsForError = ({ error, isRootErrorBoundaryExist, }) => {
|
|
9
|
+
let httpStatus;
|
|
10
|
+
let logLevel;
|
|
11
|
+
let logEvent;
|
|
12
|
+
let logMessage;
|
|
13
|
+
if (errors.isNotFoundError(error)) {
|
|
14
|
+
httpStatus = error.httpStatus || 404;
|
|
15
|
+
logLevel = 'info';
|
|
16
|
+
logEvent = 'not-found-error';
|
|
17
|
+
logMessage = `NotFoundError, action execution will be aborted.
|
|
18
|
+
Not Found page is common use-case with this error - https://tramvai.dev/docs/features/routing/wildcard-routes/#not-found-page`;
|
|
19
|
+
}
|
|
20
|
+
else if (errors.isHttpError(error)) {
|
|
21
|
+
httpStatus = error.httpStatus || 500;
|
|
22
|
+
if (error.httpStatus >= 500) {
|
|
23
|
+
logLevel = 'error';
|
|
24
|
+
logEvent = 'send-server-error';
|
|
25
|
+
logMessage = `This is expected server error, here is most common cases:
|
|
26
|
+
- Router Guard blocked request - https://tramvai.dev/docs/features/routing/hooks-and-guards#guards
|
|
27
|
+
- Forced Page Error Boundary render with 5xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
logLevel = 'info';
|
|
31
|
+
logEvent = 'http-error';
|
|
32
|
+
logMessage = `This is expected server error, here is most common cases:
|
|
33
|
+
- Route is not found - https://tramvai.dev/docs/features/routing/flow#server-navigation
|
|
34
|
+
- Forced Page Error Boundary render with 4xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action
|
|
35
|
+
- Request Limiter blocked request with 429 code - https://tramvai.dev/docs/references/modules/request-limiter/`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
httpStatus = error.statusCode || 500;
|
|
40
|
+
if (error.statusCode >= 500) {
|
|
41
|
+
logLevel = 'error';
|
|
42
|
+
logEvent = 'send-server-error';
|
|
43
|
+
logMessage = `This is Fastify 5xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
44
|
+
}
|
|
45
|
+
else if (error.statusCode >= 400) {
|
|
46
|
+
// a lot of noise with FST_ERR_CTP_INVALID_MEDIA_TYPE 4xx logs from Fastify,
|
|
47
|
+
// when somebody tries to scan our site and send some unsupported content types
|
|
48
|
+
logLevel = 'info';
|
|
49
|
+
logEvent = 'fastify-error-4xx';
|
|
50
|
+
logMessage = `This is Fastify 4xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
logLevel = 'error';
|
|
54
|
+
logEvent = 'send-server-error';
|
|
55
|
+
logMessage = `Unexpected server error. Error cause will be in "error" parameter.
|
|
56
|
+
Most likely an error has occurred in the rendering of the current React page component
|
|
57
|
+
You can try to find relative logs by using "x-request-id" header`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
logMessage = `${logMessage}
|
|
61
|
+
${isRootErrorBoundaryExist
|
|
62
|
+
? 'Root Error Boundary will be rendered for the client'
|
|
63
|
+
: 'You can add Error Boundary for better UX - https://tramvai.dev/docs/features/error-boundaries'}'`;
|
|
64
|
+
return { logMessage, logLevel, logEvent, httpStatus };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
exports.prepareLogsForError = prepareLogsForError;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AnyError } from '@tramvai/safe-strings';
|
|
2
|
+
import type { WebpackStats } from '@tramvai/tokens-render';
|
|
3
|
+
import type { ErrorBoundaryComponent } from '@tramvai/react';
|
|
4
|
+
export declare const renderErrorBoundaryPageToString: ({ element, webpackStats, requestUrl, httpStatus, error, }: {
|
|
5
|
+
element: ErrorBoundaryComponent;
|
|
6
|
+
webpackStats: WebpackStats;
|
|
7
|
+
requestUrl: string;
|
|
8
|
+
httpStatus: number;
|
|
9
|
+
error: AnyError;
|
|
10
|
+
}) => string;
|
|
11
|
+
//# sourceMappingURL=renderErrorBoundaryPageToString.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ChunkExtractor } from '@loadable/server';
|
|
2
|
+
import { parse } from '@tinkoff/url';
|
|
3
|
+
import { renderToString } from 'react-dom/server';
|
|
4
|
+
import { createElement } from 'react';
|
|
5
|
+
import { safeStringify } from '@tramvai/safe-strings';
|
|
6
|
+
|
|
7
|
+
const renderErrorBoundaryPageToString = ({ element, webpackStats, requestUrl, httpStatus, error, }) => {
|
|
8
|
+
const extractor = new ChunkExtractor({ stats: webpackStats, entrypoints: ['rootErrorBoundary'] });
|
|
9
|
+
const url = parse(requestUrl);
|
|
10
|
+
const serializedError = {
|
|
11
|
+
name: error.name,
|
|
12
|
+
status: httpStatus,
|
|
13
|
+
message: error.message,
|
|
14
|
+
stack: error.stack,
|
|
15
|
+
};
|
|
16
|
+
return renderToString(createElement(element, { error: serializedError, url })).replace('</head>', [
|
|
17
|
+
'<script>' +
|
|
18
|
+
`window.serverUrl = ${safeStringify(url)};` +
|
|
19
|
+
`window.serverError = new Error(${safeStringify(serializedError.message)});` +
|
|
20
|
+
`Object.assign(window.serverError, ${safeStringify(serializedError)});` +
|
|
21
|
+
'</script>',
|
|
22
|
+
extractor.getStyleTags(),
|
|
23
|
+
extractor.getScriptTags(),
|
|
24
|
+
'</head>',
|
|
25
|
+
]
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.join('\n'));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { renderErrorBoundaryPageToString };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var server = require('@loadable/server');
|
|
6
|
+
var url = require('@tinkoff/url');
|
|
7
|
+
var server$1 = require('react-dom/server');
|
|
8
|
+
var react = require('react');
|
|
9
|
+
var safeStrings = require('@tramvai/safe-strings');
|
|
10
|
+
|
|
11
|
+
const renderErrorBoundaryPageToString = ({ element, webpackStats, requestUrl, httpStatus, error, }) => {
|
|
12
|
+
const extractor = new server.ChunkExtractor({ stats: webpackStats, entrypoints: ['rootErrorBoundary'] });
|
|
13
|
+
const url$1 = url.parse(requestUrl);
|
|
14
|
+
const serializedError = {
|
|
15
|
+
name: error.name,
|
|
16
|
+
status: httpStatus,
|
|
17
|
+
message: error.message,
|
|
18
|
+
stack: error.stack,
|
|
19
|
+
};
|
|
20
|
+
return server$1.renderToString(react.createElement(element, { error: serializedError, url: url$1 })).replace('</head>', [
|
|
21
|
+
'<script>' +
|
|
22
|
+
`window.serverUrl = ${safeStrings.safeStringify(url$1)};` +
|
|
23
|
+
`window.serverError = new Error(${safeStrings.safeStringify(serializedError.message)});` +
|
|
24
|
+
`Object.assign(window.serverError, ${safeStrings.safeStringify(serializedError)});` +
|
|
25
|
+
'</script>',
|
|
26
|
+
extractor.getStyleTags(),
|
|
27
|
+
extractor.getScriptTags(),
|
|
28
|
+
'</head>',
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join('\n'));
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
exports.renderErrorBoundaryPageToString = renderErrorBoundaryPageToString;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function serveRootErrorBoundary({ response, app, }) {
|
|
2
|
+
app.register(async (instance) => {
|
|
3
|
+
instance.all('/_errors/5xx', (request, reply) => {
|
|
4
|
+
reply.status(200);
|
|
5
|
+
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
6
|
+
reply.header('Content-Length', Buffer.byteLength(response, 'utf8'));
|
|
7
|
+
reply.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
8
|
+
return response;
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export { serveRootErrorBoundary };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
function serveRootErrorBoundary({ response, app, }) {
|
|
6
|
+
app.register(async (instance) => {
|
|
7
|
+
instance.all('/_errors/5xx', (request, reply) => {
|
|
8
|
+
reply.status(200);
|
|
9
|
+
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
10
|
+
reply.header('Content-Length', Buffer.byteLength(response, 'utf8'));
|
|
11
|
+
reply.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
12
|
+
return response;
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.serveRootErrorBoundary = serveRootErrorBoundary;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ErrorBoundaryComponent } from '@tramvai/react';
|
|
2
|
+
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
3
|
+
import type { WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN } from '@tramvai/tokens-server-private';
|
|
4
|
+
import type { FastifyError, FastifyReply, FastifyRequest } from 'fastify';
|
|
5
|
+
export declare const getRootErrorBoundary: () => ErrorBoundaryComponent | null;
|
|
6
|
+
export declare const runHandlersFactory: (error: FastifyError, request: FastifyRequest, reply: FastifyReply) => (handlers: ExtractDependencyType<typeof WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN>) => Promise<string>;
|
|
7
|
+
export declare const getRequestInfo: (request: FastifyRequest) => {
|
|
8
|
+
ip: string;
|
|
9
|
+
requestId: string | string[];
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const getRootErrorBoundary = () => {
|
|
2
|
+
try {
|
|
3
|
+
// In case of direct `require` by path, e.g.
|
|
4
|
+
// `require(path.resolve(process.cwd(), 'src', 'error.tsx'))` file
|
|
5
|
+
// doesn't include in the bundle, that is why we are using a
|
|
6
|
+
// path alias here along with webpack config option.
|
|
7
|
+
// See usage of `ROOT_ERROR_BOUNDARY_ALIAS`.
|
|
8
|
+
// eslint-disable-next-line import/no-unresolved, import/extensions
|
|
9
|
+
const RootErrorBoundary = require('@/__private__/error').default;
|
|
10
|
+
return RootErrorBoundary;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const runHandlersFactory = (error, request, reply) => async (handlers) => {
|
|
17
|
+
if (handlers) {
|
|
18
|
+
for (const handler of handlers) {
|
|
19
|
+
const result = await handler(error, request, reply);
|
|
20
|
+
if (result) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const getRequestInfo = (request) => ({
|
|
27
|
+
ip: request.ip,
|
|
28
|
+
requestId: request.headers['x-request-id'],
|
|
29
|
+
url: request.url,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export { getRequestInfo, getRootErrorBoundary, runHandlersFactory };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const getRootErrorBoundary = () => {
|
|
6
|
+
try {
|
|
7
|
+
// In case of direct `require` by path, e.g.
|
|
8
|
+
// `require(path.resolve(process.cwd(), 'src', 'error.tsx'))` file
|
|
9
|
+
// doesn't include in the bundle, that is why we are using a
|
|
10
|
+
// path alias here along with webpack config option.
|
|
11
|
+
// See usage of `ROOT_ERROR_BOUNDARY_ALIAS`.
|
|
12
|
+
// eslint-disable-next-line import/no-unresolved, import/extensions
|
|
13
|
+
const RootErrorBoundary = require('@/__private__/error').default;
|
|
14
|
+
return RootErrorBoundary;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const runHandlersFactory = (error, request, reply) => async (handlers) => {
|
|
21
|
+
if (handlers) {
|
|
22
|
+
for (const handler of handlers) {
|
|
23
|
+
const result = await handler(error, request, reply);
|
|
24
|
+
if (result) {
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const getRequestInfo = (request) => ({
|
|
31
|
+
ip: request.ip,
|
|
32
|
+
requestId: request.headers['x-request-id'],
|
|
33
|
+
url: request.url,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
exports.getRequestInfo = getRequestInfo;
|
|
37
|
+
exports.getRootErrorBoundary = getRootErrorBoundary;
|
|
38
|
+
exports.runHandlersFactory = runHandlersFactory;
|
package/lib/server/webApp.d.ts
CHANGED
|
@@ -5,10 +5,11 @@ import type { SERVER_TOKEN } from '@tramvai/tokens-server';
|
|
|
5
5
|
import type { WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN } from '@tramvai/tokens-server-private';
|
|
6
6
|
import { type FETCH_WEBPACK_STATS_TOKEN } from '@tramvai/tokens-render';
|
|
7
7
|
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
8
|
+
import type { STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN } from '@tramvai/tokens-server';
|
|
8
9
|
export declare const webAppFactory: ({ server }: {
|
|
9
10
|
server: typeof SERVER_TOKEN;
|
|
10
11
|
}) => import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault> & PromiseLike<import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>>;
|
|
11
|
-
export declare const webAppInitCommand: ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, }: {
|
|
12
|
+
export declare const webAppInitCommand: ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }: {
|
|
12
13
|
app: ExtractDependencyType<typeof WEB_FASTIFY_APP_TOKEN>;
|
|
13
14
|
logger: ExtractDependencyType<typeof LOGGER_TOKEN>;
|
|
14
15
|
commandLineRunner: ExtractDependencyType<typeof COMMAND_LINE_RUNNER_TOKEN>;
|
|
@@ -21,5 +22,6 @@ export declare const webAppInitCommand: ({ app, logger, commandLineRunner, execu
|
|
|
21
22
|
beforeError: ExtractDependencyType<typeof WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN>;
|
|
22
23
|
afterError: ExtractDependencyType<typeof WEB_FASTIFY_APP_AFTER_ERROR_TOKEN>;
|
|
23
24
|
fetchWebpackStats: ExtractDependencyType<typeof FETCH_WEBPACK_STATS_TOKEN>;
|
|
25
|
+
staticRootErrorBoundaryError: ExtractDependencyType<typeof STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN>;
|
|
24
26
|
}) => () => Promise<void>;
|
|
25
27
|
//# sourceMappingURL=webApp.d.ts.map
|
package/lib/server/webApp.es.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Scope } from '@tramvai/core';
|
|
|
6
6
|
import { FASTIFY_REQUEST, FASTIFY_RESPONSE, SERVER_RESPONSE_STREAM, SERVER_RESPONSE_TASK_MANAGER } from '@tramvai/tokens-server-private';
|
|
7
7
|
import { REACT_SERVER_RENDER_MODE } from '@tramvai/tokens-render';
|
|
8
8
|
import { provide, optional } from '@tinkoff/dippy';
|
|
9
|
-
import { errorHandler } from './error.es.js';
|
|
9
|
+
import { errorHandler } from './error/index.es.js';
|
|
10
10
|
|
|
11
11
|
const webAppFactory = ({ server }) => {
|
|
12
12
|
const app = fastify({
|
|
@@ -19,13 +19,19 @@ const webAppFactory = ({ server }) => {
|
|
|
19
19
|
});
|
|
20
20
|
return app;
|
|
21
21
|
};
|
|
22
|
-
const webAppInitCommand = ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, }) => {
|
|
22
|
+
const webAppInitCommand = ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }) => {
|
|
23
23
|
const log = logger('server:webapp');
|
|
24
24
|
const runHandlers = (instance, handlers) => {
|
|
25
25
|
return Promise.all([handlers && Promise.all(handlers.map((handler) => handler(instance)))]);
|
|
26
26
|
};
|
|
27
27
|
return async function webAppInit() {
|
|
28
|
-
errorHandler(app, {
|
|
28
|
+
await errorHandler(app, {
|
|
29
|
+
log,
|
|
30
|
+
beforeError,
|
|
31
|
+
afterError,
|
|
32
|
+
fetchWebpackStats,
|
|
33
|
+
staticRootErrorBoundaryError,
|
|
34
|
+
});
|
|
29
35
|
await app.register(async (instance) => {
|
|
30
36
|
await runHandlers(instance, beforeInit);
|
|
31
37
|
});
|
package/lib/server/webApp.js
CHANGED
|
@@ -10,7 +10,7 @@ var core = require('@tramvai/core');
|
|
|
10
10
|
var tokensServerPrivate = require('@tramvai/tokens-server-private');
|
|
11
11
|
var tokensRender = require('@tramvai/tokens-render');
|
|
12
12
|
var dippy = require('@tinkoff/dippy');
|
|
13
|
-
var
|
|
13
|
+
var index = require('./error/index.js');
|
|
14
14
|
|
|
15
15
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
16
16
|
|
|
@@ -28,13 +28,19 @@ const webAppFactory = ({ server }) => {
|
|
|
28
28
|
});
|
|
29
29
|
return app;
|
|
30
30
|
};
|
|
31
|
-
const webAppInitCommand = ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, }) => {
|
|
31
|
+
const webAppInitCommand = ({ app, logger, commandLineRunner, executionContextManager, beforeInit, requestMetrics, limiterRequest, init, afterInit, beforeError, afterError, fetchWebpackStats, staticRootErrorBoundaryError, }) => {
|
|
32
32
|
const log = logger('server:webapp');
|
|
33
33
|
const runHandlers = (instance, handlers) => {
|
|
34
34
|
return Promise.all([handlers && Promise.all(handlers.map((handler) => handler(instance)))]);
|
|
35
35
|
};
|
|
36
36
|
return async function webAppInit() {
|
|
37
|
-
|
|
37
|
+
await index.errorHandler(app, {
|
|
38
|
+
log,
|
|
39
|
+
beforeError,
|
|
40
|
+
afterError,
|
|
41
|
+
fetchWebpackStats,
|
|
42
|
+
staticRootErrorBoundaryError,
|
|
43
|
+
});
|
|
38
44
|
await app.register(async (instance) => {
|
|
39
45
|
await runHandlers(instance, beforeInit);
|
|
40
46
|
});
|
package/lib/server.es.js
CHANGED
|
@@ -2,7 +2,7 @@ import { __decorate } from 'tslib';
|
|
|
2
2
|
import { setDefaultResultOrder } from 'dns';
|
|
3
3
|
import EventEmitter from 'events';
|
|
4
4
|
import { Module, provide, Scope, commandLineListTokens, COMMAND_LINE_RUNNER_TOKEN, APP_INFO_TOKEN } from '@tramvai/core';
|
|
5
|
-
import { SERVER_TOKEN } from '@tramvai/tokens-server';
|
|
5
|
+
import { SERVER_TOKEN, STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN } from '@tramvai/tokens-server';
|
|
6
6
|
export * from '@tramvai/tokens-server';
|
|
7
7
|
import { FETCH_WEBPACK_STATS_TOKEN } from '@tramvai/tokens-render';
|
|
8
8
|
import { SERVER_FACTORY_TOKEN, WEB_FASTIFY_APP_FACTORY_TOKEN, WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN, SERVER_RESPONSE_STREAM, SERVER_RESPONSE_TASK_MANAGER } from '@tramvai/tokens-server-private';
|
|
@@ -97,6 +97,10 @@ ServerModule = __decorate([
|
|
|
97
97
|
beforeError: { token: WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, optional: true },
|
|
98
98
|
afterError: { token: WEB_FASTIFY_APP_AFTER_ERROR_TOKEN, optional: true },
|
|
99
99
|
fetchWebpackStats: FETCH_WEBPACK_STATS_TOKEN,
|
|
100
|
+
staticRootErrorBoundaryError: {
|
|
101
|
+
token: STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN,
|
|
102
|
+
optional: true,
|
|
103
|
+
},
|
|
100
104
|
},
|
|
101
105
|
},
|
|
102
106
|
provide({
|
package/lib/server.js
CHANGED
|
@@ -104,6 +104,10 @@ exports.ServerModule = tslib.__decorate([
|
|
|
104
104
|
beforeError: { token: tokensServerPrivate.WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, optional: true },
|
|
105
105
|
afterError: { token: tokensServerPrivate.WEB_FASTIFY_APP_AFTER_ERROR_TOKEN, optional: true },
|
|
106
106
|
fetchWebpackStats: tokensRender.FETCH_WEBPACK_STATS_TOKEN,
|
|
107
|
+
staticRootErrorBoundaryError: {
|
|
108
|
+
token: tokensServer.STATIC_ROOT_ERROR_BOUNDARY_ERROR_TOKEN,
|
|
109
|
+
optional: true,
|
|
110
|
+
},
|
|
107
111
|
},
|
|
108
112
|
},
|
|
109
113
|
core.provide({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
"@tinkoff/monkeypatch": "3.0.1",
|
|
27
27
|
"@tinkoff/terminus": "0.2.1",
|
|
28
28
|
"@tinkoff/url": "0.9.1",
|
|
29
|
-
"@tramvai/module-cache-warmup": "3.
|
|
30
|
-
"@tramvai/module-metrics": "3.
|
|
31
|
-
"@tramvai/papi": "3.
|
|
32
|
-
"@tramvai/tokens-server": "3.
|
|
33
|
-
"@tramvai/tokens-router": "3.
|
|
34
|
-
"@tramvai/tokens-server-private": "3.
|
|
29
|
+
"@tramvai/module-cache-warmup": "3.10.0",
|
|
30
|
+
"@tramvai/module-metrics": "3.10.0",
|
|
31
|
+
"@tramvai/papi": "3.10.0",
|
|
32
|
+
"@tramvai/tokens-server": "3.10.0",
|
|
33
|
+
"@tramvai/tokens-router": "3.10.0",
|
|
34
|
+
"@tramvai/tokens-server-private": "3.10.0",
|
|
35
35
|
"@tramvai/safe-strings": "0.6.1",
|
|
36
36
|
"fastify": "^4.14.1",
|
|
37
37
|
"@fastify/cookie": "^8.3.0",
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@tinkoff/dippy": "0.9.1",
|
|
46
46
|
"@tinkoff/utils": "^2.1.2",
|
|
47
|
-
"@tramvai/cli": "3.
|
|
48
|
-
"@tramvai/core": "3.
|
|
49
|
-
"@tramvai/react": "3.
|
|
50
|
-
"@tramvai/module-common": "3.
|
|
51
|
-
"@tramvai/module-environment": "3.
|
|
52
|
-
"@tramvai/tokens-common": "3.
|
|
53
|
-
"@tramvai/tokens-core-private": "3.
|
|
54
|
-
"@tramvai/tokens-render": "3.
|
|
47
|
+
"@tramvai/cli": "3.10.0",
|
|
48
|
+
"@tramvai/core": "3.10.0",
|
|
49
|
+
"@tramvai/react": "3.10.0",
|
|
50
|
+
"@tramvai/module-common": "3.10.0",
|
|
51
|
+
"@tramvai/module-environment": "3.10.0",
|
|
52
|
+
"@tramvai/tokens-common": "3.10.0",
|
|
53
|
+
"@tramvai/tokens-core-private": "3.10.0",
|
|
54
|
+
"@tramvai/tokens-render": "3.10.0",
|
|
55
55
|
"react": ">=16.14.0",
|
|
56
56
|
"react-dom": ">=16.14.0",
|
|
57
57
|
"tslib": "^2.4.0"
|
package/lib/server/error.es.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import isNil from '@tinkoff/utils/is/nil';
|
|
2
|
-
import { createElement } from 'react';
|
|
3
|
-
import { renderToString } from 'react-dom/server';
|
|
4
|
-
import { parse } from '@tinkoff/url';
|
|
5
|
-
import { isRedirectFoundError, isNotFoundError, isHttpError } from '@tinkoff/errors';
|
|
6
|
-
import { safeStringify } from '@tramvai/safe-strings';
|
|
7
|
-
import { ChunkExtractor } from '@loadable/server';
|
|
8
|
-
|
|
9
|
-
const errorHandler = (app, { log, beforeError, afterError, fetchWebpackStats, }) => {
|
|
10
|
-
// eslint-disable-next-line max-statements
|
|
11
|
-
app.setErrorHandler(async (error, request, reply) => {
|
|
12
|
-
const runHandlers = async (handlers) => {
|
|
13
|
-
if (handlers) {
|
|
14
|
-
for (const handler of handlers) {
|
|
15
|
-
const result = await handler(error, request, reply);
|
|
16
|
-
if (result) {
|
|
17
|
-
return result;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const beforeErrorResult = await runHandlers(beforeError);
|
|
23
|
-
if (!isNil(beforeErrorResult)) {
|
|
24
|
-
return beforeErrorResult;
|
|
25
|
-
}
|
|
26
|
-
const requestInfo = {
|
|
27
|
-
ip: request.ip,
|
|
28
|
-
requestId: request.headers['x-request-id'],
|
|
29
|
-
url: request.url,
|
|
30
|
-
};
|
|
31
|
-
let RootErrorBoundary = null;
|
|
32
|
-
try {
|
|
33
|
-
// In case of direct `require` by path, e.g.
|
|
34
|
-
// `require(path.resolve(process.cwd(), 'src', 'error.tsx'))` file
|
|
35
|
-
// doesn't include in the bundle, that is why we are using a
|
|
36
|
-
// path alias here along with webpack config option.
|
|
37
|
-
// See usage of `ROOT_ERROR_BOUNDARY_ALIAS`.
|
|
38
|
-
// eslint-disable-next-line import/no-unresolved, import/extensions
|
|
39
|
-
RootErrorBoundary = require('@/__private__/error').default;
|
|
40
|
-
}
|
|
41
|
-
catch { }
|
|
42
|
-
if (isRedirectFoundError(error)) {
|
|
43
|
-
log.info({
|
|
44
|
-
event: 'redirect-found-error',
|
|
45
|
-
message: `RedirectFoundError, redirect to ${error.nextUrl}, action execution will be aborted.
|
|
46
|
-
More information about redirects - https://tramvai.dev/docs/features/routing/redirects`,
|
|
47
|
-
error,
|
|
48
|
-
requestInfo,
|
|
49
|
-
});
|
|
50
|
-
reply.header('cache-control', 'no-store, no-cache, must-revalidate');
|
|
51
|
-
reply.redirect(error.httpStatus || 307, error.nextUrl);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
let httpStatus;
|
|
55
|
-
let logLevel;
|
|
56
|
-
let logEvent;
|
|
57
|
-
let logMessage;
|
|
58
|
-
if (isNotFoundError(error)) {
|
|
59
|
-
httpStatus = error.httpStatus || 404;
|
|
60
|
-
logLevel = 'info';
|
|
61
|
-
logEvent = 'not-found-error';
|
|
62
|
-
logMessage = `NotFoundError, action execution will be aborted.
|
|
63
|
-
Not Found page is common use-case with this error - https://tramvai.dev/docs/features/routing/wildcard-routes/#not-found-page`;
|
|
64
|
-
}
|
|
65
|
-
else if (isHttpError(error)) {
|
|
66
|
-
httpStatus = error.httpStatus || 500;
|
|
67
|
-
if (error.httpStatus >= 500) {
|
|
68
|
-
logLevel = 'error';
|
|
69
|
-
logEvent = 'send-server-error';
|
|
70
|
-
logMessage = `This is expected server error, here is most common cases:
|
|
71
|
-
- Router Guard blocked request - https://tramvai.dev/docs/features/routing/hooks-and-guards#guards
|
|
72
|
-
- Forced Page Error Boundary render with 5xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action`;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
logLevel = 'info';
|
|
76
|
-
logEvent = 'http-error';
|
|
77
|
-
logMessage = `This is expected server error, here is most common cases:
|
|
78
|
-
- Route is not found - https://tramvai.dev/docs/features/routing/flow#server-navigation
|
|
79
|
-
- Forced Page Error Boundary render with 4xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action
|
|
80
|
-
- Request Limiter blocked request with 429 code - https://tramvai.dev/docs/references/modules/request-limiter/`;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
httpStatus = error.statusCode || 500;
|
|
85
|
-
if (error.statusCode >= 500) {
|
|
86
|
-
logLevel = 'error';
|
|
87
|
-
logEvent = 'send-server-error';
|
|
88
|
-
logMessage = `This is Fastify 5xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
89
|
-
}
|
|
90
|
-
else if (error.statusCode >= 400) {
|
|
91
|
-
// a lot of noise with FST_ERR_CTP_INVALID_MEDIA_TYPE 4xx logs from Fastify,
|
|
92
|
-
// when somebody tries to scan our site and send some unsupported content types
|
|
93
|
-
logLevel = 'info';
|
|
94
|
-
logEvent = 'fastify-error-4xx';
|
|
95
|
-
logMessage = `This is Fastify 4xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
logLevel = 'error';
|
|
99
|
-
logEvent = 'send-server-error';
|
|
100
|
-
logMessage = `Unexpected server error. Error cause will be in "error" parameter.
|
|
101
|
-
Most likely an error has occurred in the rendering of the current React page component
|
|
102
|
-
You can try to find relative logs by using "x-request-id" header`;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
logMessage = `${logMessage}
|
|
106
|
-
${RootErrorBoundary !== null
|
|
107
|
-
? 'Root Error Boundary will be rendered for the client'
|
|
108
|
-
: 'You can add Error Boundary for better UX - https://tramvai.dev/docs/features/error-boundaries'}'`;
|
|
109
|
-
log[logLevel]({
|
|
110
|
-
event: logEvent,
|
|
111
|
-
message: logMessage,
|
|
112
|
-
error,
|
|
113
|
-
requestInfo,
|
|
114
|
-
});
|
|
115
|
-
const afterErrorResult = await runHandlers(afterError);
|
|
116
|
-
if (!isNil(afterErrorResult)) {
|
|
117
|
-
return afterErrorResult;
|
|
118
|
-
}
|
|
119
|
-
reply.status(httpStatus);
|
|
120
|
-
if (RootErrorBoundary !== null) {
|
|
121
|
-
try {
|
|
122
|
-
const stats = await fetchWebpackStats();
|
|
123
|
-
const extractor = new ChunkExtractor({ stats, entrypoints: ['rootErrorBoundary'] });
|
|
124
|
-
const url = parse(requestInfo.url);
|
|
125
|
-
const serializedError = {
|
|
126
|
-
status: httpStatus,
|
|
127
|
-
message: error.message,
|
|
128
|
-
stack: error.stack,
|
|
129
|
-
};
|
|
130
|
-
const body = renderToString(createElement(RootErrorBoundary, { error: serializedError, url })).replace('</head>', [
|
|
131
|
-
'<script>' +
|
|
132
|
-
`window.serverUrl = ${safeStringify(url)};` +
|
|
133
|
-
`window.serverError = new Error(${safeStringify(serializedError.message)});` +
|
|
134
|
-
`Object.assign(window.serverError, ${safeStringify(serializedError)});` +
|
|
135
|
-
'</script>',
|
|
136
|
-
extractor.getStyleTags(),
|
|
137
|
-
extractor.getScriptTags(),
|
|
138
|
-
'</head>',
|
|
139
|
-
]
|
|
140
|
-
.filter(Boolean)
|
|
141
|
-
.join('\n'));
|
|
142
|
-
log.info({
|
|
143
|
-
event: 'render-root-error-boundary',
|
|
144
|
-
message: 'Render Root Error Boundary for the client',
|
|
145
|
-
});
|
|
146
|
-
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
147
|
-
reply.header('Content-Length', Buffer.byteLength(body, 'utf8'));
|
|
148
|
-
reply.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
149
|
-
return body;
|
|
150
|
-
}
|
|
151
|
-
catch (e) {
|
|
152
|
-
log.warn({
|
|
153
|
-
event: 'failed-root-error-boundary',
|
|
154
|
-
message: 'Root Error Boundary rendering failed',
|
|
155
|
-
error: e,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
export { errorHandler };
|
package/lib/server/error.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var isNil = require('@tinkoff/utils/is/nil');
|
|
6
|
-
var react = require('react');
|
|
7
|
-
var server$1 = require('react-dom/server');
|
|
8
|
-
var url = require('@tinkoff/url');
|
|
9
|
-
var errors = require('@tinkoff/errors');
|
|
10
|
-
var safeStrings = require('@tramvai/safe-strings');
|
|
11
|
-
var server = require('@loadable/server');
|
|
12
|
-
|
|
13
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
14
|
-
|
|
15
|
-
var isNil__default = /*#__PURE__*/_interopDefaultLegacy(isNil);
|
|
16
|
-
|
|
17
|
-
const errorHandler = (app, { log, beforeError, afterError, fetchWebpackStats, }) => {
|
|
18
|
-
// eslint-disable-next-line max-statements
|
|
19
|
-
app.setErrorHandler(async (error, request, reply) => {
|
|
20
|
-
const runHandlers = async (handlers) => {
|
|
21
|
-
if (handlers) {
|
|
22
|
-
for (const handler of handlers) {
|
|
23
|
-
const result = await handler(error, request, reply);
|
|
24
|
-
if (result) {
|
|
25
|
-
return result;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
const beforeErrorResult = await runHandlers(beforeError);
|
|
31
|
-
if (!isNil__default["default"](beforeErrorResult)) {
|
|
32
|
-
return beforeErrorResult;
|
|
33
|
-
}
|
|
34
|
-
const requestInfo = {
|
|
35
|
-
ip: request.ip,
|
|
36
|
-
requestId: request.headers['x-request-id'],
|
|
37
|
-
url: request.url,
|
|
38
|
-
};
|
|
39
|
-
let RootErrorBoundary = null;
|
|
40
|
-
try {
|
|
41
|
-
// In case of direct `require` by path, e.g.
|
|
42
|
-
// `require(path.resolve(process.cwd(), 'src', 'error.tsx'))` file
|
|
43
|
-
// doesn't include in the bundle, that is why we are using a
|
|
44
|
-
// path alias here along with webpack config option.
|
|
45
|
-
// See usage of `ROOT_ERROR_BOUNDARY_ALIAS`.
|
|
46
|
-
// eslint-disable-next-line import/no-unresolved, import/extensions
|
|
47
|
-
RootErrorBoundary = require('@/__private__/error').default;
|
|
48
|
-
}
|
|
49
|
-
catch { }
|
|
50
|
-
if (errors.isRedirectFoundError(error)) {
|
|
51
|
-
log.info({
|
|
52
|
-
event: 'redirect-found-error',
|
|
53
|
-
message: `RedirectFoundError, redirect to ${error.nextUrl}, action execution will be aborted.
|
|
54
|
-
More information about redirects - https://tramvai.dev/docs/features/routing/redirects`,
|
|
55
|
-
error,
|
|
56
|
-
requestInfo,
|
|
57
|
-
});
|
|
58
|
-
reply.header('cache-control', 'no-store, no-cache, must-revalidate');
|
|
59
|
-
reply.redirect(error.httpStatus || 307, error.nextUrl);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
let httpStatus;
|
|
63
|
-
let logLevel;
|
|
64
|
-
let logEvent;
|
|
65
|
-
let logMessage;
|
|
66
|
-
if (errors.isNotFoundError(error)) {
|
|
67
|
-
httpStatus = error.httpStatus || 404;
|
|
68
|
-
logLevel = 'info';
|
|
69
|
-
logEvent = 'not-found-error';
|
|
70
|
-
logMessage = `NotFoundError, action execution will be aborted.
|
|
71
|
-
Not Found page is common use-case with this error - https://tramvai.dev/docs/features/routing/wildcard-routes/#not-found-page`;
|
|
72
|
-
}
|
|
73
|
-
else if (errors.isHttpError(error)) {
|
|
74
|
-
httpStatus = error.httpStatus || 500;
|
|
75
|
-
if (error.httpStatus >= 500) {
|
|
76
|
-
logLevel = 'error';
|
|
77
|
-
logEvent = 'send-server-error';
|
|
78
|
-
logMessage = `This is expected server error, here is most common cases:
|
|
79
|
-
- Router Guard blocked request - https://tramvai.dev/docs/features/routing/hooks-and-guards#guards
|
|
80
|
-
- Forced Page Error Boundary render with 5xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action`;
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
logLevel = 'info';
|
|
84
|
-
logEvent = 'http-error';
|
|
85
|
-
logMessage = `This is expected server error, here is most common cases:
|
|
86
|
-
- Route is not found - https://tramvai.dev/docs/features/routing/flow#server-navigation
|
|
87
|
-
- Forced Page Error Boundary render with 4xx code in Guard or Action - https://tramvai.dev/docs/features/error-boundaries#force-render-page-error-boundary-in-action
|
|
88
|
-
- Request Limiter blocked request with 429 code - https://tramvai.dev/docs/references/modules/request-limiter/`;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
httpStatus = error.statusCode || 500;
|
|
93
|
-
if (error.statusCode >= 500) {
|
|
94
|
-
logLevel = 'error';
|
|
95
|
-
logEvent = 'send-server-error';
|
|
96
|
-
logMessage = `This is Fastify 5xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
97
|
-
}
|
|
98
|
-
else if (error.statusCode >= 400) {
|
|
99
|
-
// a lot of noise with FST_ERR_CTP_INVALID_MEDIA_TYPE 4xx logs from Fastify,
|
|
100
|
-
// when somebody tries to scan our site and send some unsupported content types
|
|
101
|
-
logLevel = 'info';
|
|
102
|
-
logEvent = 'fastify-error-4xx';
|
|
103
|
-
logMessage = `This is Fastify 4xx error, you can check ${error.code} code in https://www.fastify.io/docs/latest/Reference/Errors/#${error.code.toLowerCase()} page`;
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
logLevel = 'error';
|
|
107
|
-
logEvent = 'send-server-error';
|
|
108
|
-
logMessage = `Unexpected server error. Error cause will be in "error" parameter.
|
|
109
|
-
Most likely an error has occurred in the rendering of the current React page component
|
|
110
|
-
You can try to find relative logs by using "x-request-id" header`;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
logMessage = `${logMessage}
|
|
114
|
-
${RootErrorBoundary !== null
|
|
115
|
-
? 'Root Error Boundary will be rendered for the client'
|
|
116
|
-
: 'You can add Error Boundary for better UX - https://tramvai.dev/docs/features/error-boundaries'}'`;
|
|
117
|
-
log[logLevel]({
|
|
118
|
-
event: logEvent,
|
|
119
|
-
message: logMessage,
|
|
120
|
-
error,
|
|
121
|
-
requestInfo,
|
|
122
|
-
});
|
|
123
|
-
const afterErrorResult = await runHandlers(afterError);
|
|
124
|
-
if (!isNil__default["default"](afterErrorResult)) {
|
|
125
|
-
return afterErrorResult;
|
|
126
|
-
}
|
|
127
|
-
reply.status(httpStatus);
|
|
128
|
-
if (RootErrorBoundary !== null) {
|
|
129
|
-
try {
|
|
130
|
-
const stats = await fetchWebpackStats();
|
|
131
|
-
const extractor = new server.ChunkExtractor({ stats, entrypoints: ['rootErrorBoundary'] });
|
|
132
|
-
const url$1 = url.parse(requestInfo.url);
|
|
133
|
-
const serializedError = {
|
|
134
|
-
status: httpStatus,
|
|
135
|
-
message: error.message,
|
|
136
|
-
stack: error.stack,
|
|
137
|
-
};
|
|
138
|
-
const body = server$1.renderToString(react.createElement(RootErrorBoundary, { error: serializedError, url: url$1 })).replace('</head>', [
|
|
139
|
-
'<script>' +
|
|
140
|
-
`window.serverUrl = ${safeStrings.safeStringify(url$1)};` +
|
|
141
|
-
`window.serverError = new Error(${safeStrings.safeStringify(serializedError.message)});` +
|
|
142
|
-
`Object.assign(window.serverError, ${safeStrings.safeStringify(serializedError)});` +
|
|
143
|
-
'</script>',
|
|
144
|
-
extractor.getStyleTags(),
|
|
145
|
-
extractor.getScriptTags(),
|
|
146
|
-
'</head>',
|
|
147
|
-
]
|
|
148
|
-
.filter(Boolean)
|
|
149
|
-
.join('\n'));
|
|
150
|
-
log.info({
|
|
151
|
-
event: 'render-root-error-boundary',
|
|
152
|
-
message: 'Render Root Error Boundary for the client',
|
|
153
|
-
});
|
|
154
|
-
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
155
|
-
reply.header('Content-Length', Buffer.byteLength(body, 'utf8'));
|
|
156
|
-
reply.header('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
157
|
-
return body;
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
log.warn({
|
|
161
|
-
event: 'failed-root-error-boundary',
|
|
162
|
-
message: 'Root Error Boundary rendering failed',
|
|
163
|
-
error: e,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
throw error;
|
|
168
|
-
});
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
exports.errorHandler = errorHandler;
|