@tramvai/module-render 2.79.9 → 2.84.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.es.js CHANGED
@@ -1,6 +1,4 @@
1
1
  import { __decorate } from 'tslib';
2
- import { createElement } from 'react';
3
- import { renderToString } from 'react-dom/server';
4
2
  import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
5
3
  import { CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
6
4
  import { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
@@ -8,10 +6,8 @@ import { ClientHintsModule, USER_AGENT_TOKEN } from '@tramvai/module-client-hint
8
6
  import { RESOURCES_REGISTRY, RESOURCE_INLINE_OPTIONS, RENDER_SLOTS, POLYFILL_CONDITION, HTML_ATTRS, MODERN_SATISFIES_TOKEN, RENDER_FLOW_AFTER_TOKEN, CUSTOM_RENDER, EXTEND_RENDER, REACT_SERVER_RENDER_MODE, ResourceType } from '@tramvai/tokens-render';
9
7
  export * from '@tramvai/tokens-render';
10
8
  import { Scope } from '@tinkoff/dippy';
11
- import { WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN } from '@tramvai/tokens-server-private';
12
- import { ROOT_ERROR_BOUNDARY_COMPONENT_TOKEN } from '@tramvai/react';
13
- import { parse } from '@tinkoff/url';
14
9
  import { satisfies } from '@tinkoff/user-agent';
10
+ import { isRedirectFoundError } from '@tinkoff/errors';
15
11
  import { setPageErrorEvent, PageErrorStore, deserializeError } from '@tramvai/module-router';
16
12
  export { PageErrorStore, setPageErrorEvent } from '@tramvai/module-router';
17
13
  import { ResourcesInliner } from './resourcesInliner/resourcesInliner.es.js';
@@ -80,43 +76,85 @@ RenderModule = RenderModule_1 = __decorate([
80
76
  }),
81
77
  provide({
82
78
  provide: commandLineListTokens.generatePage,
83
- useFactory: ({ htmlBuilder, logger, requestManager, responseManager, context }) => {
79
+ useFactory: ({ htmlBuilder, logger, requestManager, responseManager, context, pageService, }) => {
84
80
  const log = logger('module-render');
81
+ // eslint-disable-next-line max-statements
85
82
  return async function render() {
83
+ const pageErrorBoundary = pageService.resolveComponentFromConfig('errorBoundary');
86
84
  let html;
87
85
  try {
88
86
  html = await htmlBuilder.flow();
89
87
  }
90
88
  catch (error) {
91
- // assuming that there was an error when rendering the page, try to render again with ErrorBoundary
89
+ // if there is no Page Error Boundary, will try to render Root Error Boundary later in error handler
90
+ if (!pageErrorBoundary) {
91
+ throw error;
92
+ }
93
+ // assuming that there was an error when rendering the page, try to render again with Page Error Boundary
92
94
  try {
93
- log.info({ event: 'render-page-boundary-start' });
94
95
  context.dispatch(setPageErrorEvent(error));
95
96
  html = await htmlBuilder.flow();
96
- log.info({ event: 'render-page-boundary-success' });
97
+ log.info({
98
+ event: 'render-page-error-boundary',
99
+ message: 'Render Page Error Boundary for the client',
100
+ });
97
101
  }
98
102
  catch (e) {
99
- log.warn({ event: 'render-page-boundary-error', error: e });
100
- // pass page render error to default error handler,
101
- // send-server-error event will be logged with this error
103
+ log.warn({
104
+ event: 'failed-page-error-boundary',
105
+ message: 'Page Error Boundary rendering failed',
106
+ error: e,
107
+ });
108
+ // pass page render error to default error handler
102
109
  throw error;
103
110
  }
104
111
  }
105
112
  const pageRenderError = context.getState(PageErrorStore);
113
+ // if there is no Page Error Boundary and page error exists, that means that page render was interrupted and current `html` is invalid
114
+ // if it is RedirectFoundError, also pass it to default error handler
115
+ if (pageRenderError &&
116
+ (!pageErrorBoundary || isRedirectFoundError(pageRenderError))) {
117
+ throw pageRenderError;
118
+ }
106
119
  // log send-server-error only after successful Page Boundary render,
107
120
  // otherwise this event will be logged in default error handler
108
121
  if (pageRenderError) {
109
- const status = pageRenderError.status || pageRenderError.httpStatus || 500;
110
- if (status >= 500) {
111
- const requestInfo = {
112
- ip: requestManager.getClientIp(),
113
- requestId: requestManager.getHeader('x-request-id'),
114
- url: requestManager.getUrl(),
115
- };
122
+ const status = pageRenderError.httpStatus || 500;
123
+ const error = deserializeError(pageRenderError);
124
+ const requestInfo = {
125
+ ip: requestManager.getClientIp(),
126
+ requestId: requestManager.getHeader('x-request-id'),
127
+ url: requestManager.getUrl(),
128
+ };
129
+ if ('httpStatus' in pageRenderError) {
130
+ if (pageRenderError.httpStatus >= 500) {
131
+ log.error({
132
+ event: 'send-server-error',
133
+ message: `This is expected server error, here is most common cases:
134
+ - 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.
135
+ Page Error Boundary will be rendered for the client`,
136
+ error,
137
+ requestInfo,
138
+ });
139
+ }
140
+ else {
141
+ log.info({
142
+ event: 'http-error',
143
+ message: `This is expected server error, here is most common cases:
144
+ - 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.
145
+ Page Error Boundary will be rendered for the client`,
146
+ error,
147
+ requestInfo,
148
+ });
149
+ }
150
+ }
151
+ else {
116
152
  log.error({
117
153
  event: 'send-server-error',
118
- message: 'Page render error, switch to page boundary',
119
- error: deserializeError(pageRenderError),
154
+ message: `Unexpected server error. Error cause will be in "error" parameter.
155
+ Most likely an error has occurred in the rendering of the current React page component.
156
+ Page Error Boundary will be rendered for the client`,
157
+ error,
120
158
  requestInfo,
121
159
  });
122
160
  }
@@ -136,6 +174,7 @@ RenderModule = RenderModule_1 = __decorate([
136
174
  responseManager: RESPONSE_MANAGER_TOKEN,
137
175
  htmlBuilder: 'htmlBuilder',
138
176
  context: CONTEXT_TOKEN,
177
+ pageService: PAGE_SERVICE_TOKEN,
139
178
  },
140
179
  multi: true,
141
180
  }),
@@ -208,52 +247,6 @@ RenderModule = RenderModule_1 = __decorate([
208
247
  types: [ResourceType.style],
209
248
  },
210
249
  }),
211
- provide({
212
- provide: WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN,
213
- multi: true,
214
- useFactory: ({ RootErrorBoundary, logger }) => {
215
- const log = logger('module-render:error-handler');
216
- return (error, request, reply) => {
217
- if (!RootErrorBoundary) {
218
- return;
219
- }
220
- let body;
221
- try {
222
- log.info({ event: 'render-root-boundary-start' });
223
- body = renderToString(createElement(RootErrorBoundary, { error, url: parse(request.url) }));
224
- log.info({ event: 'render-root-boundary-success' });
225
- const status = error.status || error.httpStatus || 500;
226
- // log send-server-error only after successful Root Boundary render,
227
- // otherwise this event will be logged in default error handler
228
- if (status >= 500) {
229
- const requestInfo = {
230
- ip: request.headers['x-real-ip'],
231
- requestId: request.headers['x-request-id'],
232
- url: request.url,
233
- };
234
- log.error({
235
- event: 'send-server-error',
236
- message: 'Page render error, switch to root boundary',
237
- error,
238
- requestInfo,
239
- });
240
- }
241
- reply.status(status);
242
- reply.header('Content-Type', 'text/html; charset=utf-8');
243
- reply.header('Content-Length', Buffer.byteLength(body, 'utf8'));
244
- reply.header('Cache-Control', 'no-cache, no-store, must-revalidate');
245
- return body;
246
- }
247
- catch (e) {
248
- log.warn({ event: 'render-root-boundary-error', error: e });
249
- }
250
- };
251
- },
252
- deps: {
253
- RootErrorBoundary: { token: ROOT_ERROR_BOUNDARY_COMPONENT_TOKEN, optional: true },
254
- logger: LOGGER_TOKEN,
255
- },
256
- }),
257
250
  provide({
258
251
  provide: MODERN_SATISFIES_TOKEN,
259
252
  useFactory: ({ requestManager, userAgent, cache }) => {
package/lib/server.js CHANGED
@@ -3,18 +3,14 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var tslib = require('tslib');
6
- var react = require('react');
7
- var server = require('react-dom/server');
8
6
  var core = require('@tramvai/core');
9
7
  var tokensCommon = require('@tramvai/tokens-common');
10
8
  var tokensRouter = require('@tramvai/tokens-router');
11
9
  var moduleClientHints = require('@tramvai/module-client-hints');
12
10
  var tokensRender = require('@tramvai/tokens-render');
13
11
  var dippy = require('@tinkoff/dippy');
14
- var tokensServerPrivate = require('@tramvai/tokens-server-private');
15
- var react$1 = require('@tramvai/react');
16
- var url = require('@tinkoff/url');
17
12
  var userAgent = require('@tinkoff/user-agent');
13
+ var errors = require('@tinkoff/errors');
18
14
  var moduleRouter = require('@tramvai/module-router');
19
15
  var resourcesInliner = require('./resourcesInliner/resourcesInliner.js');
20
16
  var tokens = require('./resourcesInliner/tokens.js');
@@ -81,43 +77,85 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
81
77
  }),
82
78
  core.provide({
83
79
  provide: core.commandLineListTokens.generatePage,
84
- useFactory: ({ htmlBuilder, logger, requestManager, responseManager, context }) => {
80
+ useFactory: ({ htmlBuilder, logger, requestManager, responseManager, context, pageService, }) => {
85
81
  const log = logger('module-render');
82
+ // eslint-disable-next-line max-statements
86
83
  return async function render() {
84
+ const pageErrorBoundary = pageService.resolveComponentFromConfig('errorBoundary');
87
85
  let html;
88
86
  try {
89
87
  html = await htmlBuilder.flow();
90
88
  }
91
89
  catch (error) {
92
- // assuming that there was an error when rendering the page, try to render again with ErrorBoundary
90
+ // if there is no Page Error Boundary, will try to render Root Error Boundary later in error handler
91
+ if (!pageErrorBoundary) {
92
+ throw error;
93
+ }
94
+ // assuming that there was an error when rendering the page, try to render again with Page Error Boundary
93
95
  try {
94
- log.info({ event: 'render-page-boundary-start' });
95
96
  context.dispatch(moduleRouter.setPageErrorEvent(error));
96
97
  html = await htmlBuilder.flow();
97
- log.info({ event: 'render-page-boundary-success' });
98
+ log.info({
99
+ event: 'render-page-error-boundary',
100
+ message: 'Render Page Error Boundary for the client',
101
+ });
98
102
  }
99
103
  catch (e) {
100
- log.warn({ event: 'render-page-boundary-error', error: e });
101
- // pass page render error to default error handler,
102
- // send-server-error event will be logged with this error
104
+ log.warn({
105
+ event: 'failed-page-error-boundary',
106
+ message: 'Page Error Boundary rendering failed',
107
+ error: e,
108
+ });
109
+ // pass page render error to default error handler
103
110
  throw error;
104
111
  }
105
112
  }
106
113
  const pageRenderError = context.getState(moduleRouter.PageErrorStore);
114
+ // if there is no Page Error Boundary and page error exists, that means that page render was interrupted and current `html` is invalid
115
+ // if it is RedirectFoundError, also pass it to default error handler
116
+ if (pageRenderError &&
117
+ (!pageErrorBoundary || errors.isRedirectFoundError(pageRenderError))) {
118
+ throw pageRenderError;
119
+ }
107
120
  // log send-server-error only after successful Page Boundary render,
108
121
  // otherwise this event will be logged in default error handler
109
122
  if (pageRenderError) {
110
- const status = pageRenderError.status || pageRenderError.httpStatus || 500;
111
- if (status >= 500) {
112
- const requestInfo = {
113
- ip: requestManager.getClientIp(),
114
- requestId: requestManager.getHeader('x-request-id'),
115
- url: requestManager.getUrl(),
116
- };
123
+ const status = pageRenderError.httpStatus || 500;
124
+ const error = moduleRouter.deserializeError(pageRenderError);
125
+ const requestInfo = {
126
+ ip: requestManager.getClientIp(),
127
+ requestId: requestManager.getHeader('x-request-id'),
128
+ url: requestManager.getUrl(),
129
+ };
130
+ if ('httpStatus' in pageRenderError) {
131
+ if (pageRenderError.httpStatus >= 500) {
132
+ log.error({
133
+ event: 'send-server-error',
134
+ message: `This is expected server error, here is most common cases:
135
+ - 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.
136
+ Page Error Boundary will be rendered for the client`,
137
+ error,
138
+ requestInfo,
139
+ });
140
+ }
141
+ else {
142
+ log.info({
143
+ event: 'http-error',
144
+ message: `This is expected server error, here is most common cases:
145
+ - 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.
146
+ Page Error Boundary will be rendered for the client`,
147
+ error,
148
+ requestInfo,
149
+ });
150
+ }
151
+ }
152
+ else {
117
153
  log.error({
118
154
  event: 'send-server-error',
119
- message: 'Page render error, switch to page boundary',
120
- error: moduleRouter.deserializeError(pageRenderError),
155
+ message: `Unexpected server error. Error cause will be in "error" parameter.
156
+ Most likely an error has occurred in the rendering of the current React page component.
157
+ Page Error Boundary will be rendered for the client`,
158
+ error,
121
159
  requestInfo,
122
160
  });
123
161
  }
@@ -137,6 +175,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
137
175
  responseManager: tokensCommon.RESPONSE_MANAGER_TOKEN,
138
176
  htmlBuilder: 'htmlBuilder',
139
177
  context: tokensCommon.CONTEXT_TOKEN,
178
+ pageService: tokensRouter.PAGE_SERVICE_TOKEN,
140
179
  },
141
180
  multi: true,
142
181
  }),
@@ -209,52 +248,6 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
209
248
  types: [tokensRender.ResourceType.style],
210
249
  },
211
250
  }),
212
- core.provide({
213
- provide: tokensServerPrivate.WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN,
214
- multi: true,
215
- useFactory: ({ RootErrorBoundary, logger }) => {
216
- const log = logger('module-render:error-handler');
217
- return (error, request, reply) => {
218
- if (!RootErrorBoundary) {
219
- return;
220
- }
221
- let body;
222
- try {
223
- log.info({ event: 'render-root-boundary-start' });
224
- body = server.renderToString(react.createElement(RootErrorBoundary, { error, url: url.parse(request.url) }));
225
- log.info({ event: 'render-root-boundary-success' });
226
- const status = error.status || error.httpStatus || 500;
227
- // log send-server-error only after successful Root Boundary render,
228
- // otherwise this event will be logged in default error handler
229
- if (status >= 500) {
230
- const requestInfo = {
231
- ip: request.headers['x-real-ip'],
232
- requestId: request.headers['x-request-id'],
233
- url: request.url,
234
- };
235
- log.error({
236
- event: 'send-server-error',
237
- message: 'Page render error, switch to root boundary',
238
- error,
239
- requestInfo,
240
- });
241
- }
242
- reply.status(status);
243
- reply.header('Content-Type', 'text/html; charset=utf-8');
244
- reply.header('Content-Length', Buffer.byteLength(body, 'utf8'));
245
- reply.header('Cache-Control', 'no-cache, no-store, must-revalidate');
246
- return body;
247
- }
248
- catch (e) {
249
- log.warn({ event: 'render-root-boundary-error', error: e });
250
- }
251
- };
252
- },
253
- deps: {
254
- RootErrorBoundary: { token: react$1.ROOT_ERROR_BOUNDARY_COMPONENT_TOKEN, optional: true },
255
- logger: tokensCommon.LOGGER_TOKEN,
256
- },
257
- }),
258
251
  core.provide({
259
252
  provide: tokensRender.MODERN_SATISFIES_TOKEN,
260
253
  useFactory: ({ requestManager, userAgent: userAgent$1, cache }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-render",
3
- "version": "2.79.9",
3
+ "version": "2.84.0",
4
4
  "description": "",
5
5
  "browser": "lib/browser.js",
6
6
  "main": "lib/server.js",
@@ -24,14 +24,15 @@
24
24
  "@loadable/server": "^5.15.0",
25
25
  "@tinkoff/htmlpagebuilder": "0.5.8",
26
26
  "@tinkoff/layout-factory": "0.3.8",
27
+ "@tinkoff/errors": "0.3.8",
27
28
  "@tinkoff/url": "0.8.6",
28
- "@tinkoff/user-agent": "0.4.204",
29
- "@tramvai/module-client-hints": "2.79.9",
30
- "@tramvai/module-router": "2.79.9",
31
- "@tramvai/react": "2.79.9",
29
+ "@tinkoff/user-agent": "0.4.214",
30
+ "@tramvai/module-client-hints": "2.84.0",
31
+ "@tramvai/module-router": "2.84.0",
32
+ "@tramvai/react": "2.84.0",
32
33
  "@tramvai/safe-strings": "0.5.8",
33
- "@tramvai/tokens-render": "2.79.9",
34
- "@tramvai/experiments": "2.79.9",
34
+ "@tramvai/tokens-render": "2.84.0",
35
+ "@tramvai/experiments": "2.84.0",
35
36
  "@types/loadable__server": "^5.12.6",
36
37
  "node-fetch": "^2.6.1"
37
38
  },
@@ -39,14 +40,14 @@
39
40
  "@tinkoff/dippy": "0.8.15",
40
41
  "@tinkoff/utils": "^2.1.2",
41
42
  "@tinkoff/react-hooks": "0.1.6",
42
- "@tramvai/cli": "2.79.9",
43
- "@tramvai/core": "2.79.9",
44
- "@tramvai/module-common": "2.79.9",
45
- "@tramvai/state": "2.79.9",
46
- "@tramvai/test-helpers": "2.79.9",
47
- "@tramvai/tokens-common": "2.79.9",
48
- "@tramvai/tokens-router": "2.79.9",
49
- "@tramvai/tokens-server-private": "2.79.9",
43
+ "@tramvai/cli": "2.84.0",
44
+ "@tramvai/core": "2.84.0",
45
+ "@tramvai/module-common": "2.84.0",
46
+ "@tramvai/state": "2.84.0",
47
+ "@tramvai/test-helpers": "2.84.0",
48
+ "@tramvai/tokens-common": "2.84.0",
49
+ "@tramvai/tokens-router": "2.84.0",
50
+ "@tramvai/tokens-server-private": "2.84.0",
50
51
  "express": "^4.17.1",
51
52
  "prop-types": "^15.6.2",
52
53
  "react": ">=16.14.0",