@tramvai/module-render 3.17.0 → 3.19.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.
@@ -1,19 +1,23 @@
1
1
  import type { ExtractDependencyType } from '@tinkoff/dippy';
2
2
  import type { DI_TOKEN } from '@tramvai/core';
3
- import type { CONTEXT_TOKEN, LOGGER_TOKEN } from '@tramvai/module-common';
4
- import type { EXTEND_RENDER, CUSTOM_RENDER, REACT_SERVER_RENDER_MODE, WebpackStats } from '@tramvai/tokens-render';
3
+ import type { CONTEXT_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN, LOGGER_TOKEN } from '@tramvai/module-common';
4
+ import type { EXTEND_RENDER, CUSTOM_RENDER, REACT_SERVER_RENDER_MODE, WebpackStats, REACT_STREAMING_RENDER_TIMEOUT } from '@tramvai/tokens-render';
5
5
  import type { ChunkExtractor } from '@loadable/server';
6
6
  import type { SERVER_RESPONSE_STREAM, SERVER_RESPONSE_TASK_MANAGER } from '@tramvai/tokens-server-private';
7
+ type StreamingTimeout = ExtractDependencyType<typeof REACT_STREAMING_RENDER_TIMEOUT>;
8
+ type DeferredActions = ExtractDependencyType<typeof DEFERRED_ACTIONS_MAP_TOKEN>;
7
9
  export declare class ReactRenderServer {
8
10
  customRender: typeof CUSTOM_RENDER;
9
11
  extendRender: ExtractDependencyType<typeof EXTEND_RENDER>;
10
12
  context: typeof CONTEXT_TOKEN;
13
+ deferredActions: DeferredActions;
11
14
  di: typeof DI_TOKEN;
12
15
  log: ReturnType<typeof LOGGER_TOKEN>;
13
16
  responseTaskManager: typeof SERVER_RESPONSE_TASK_MANAGER;
14
17
  responseStream: typeof SERVER_RESPONSE_STREAM;
18
+ streamingTimeout: StreamingTimeout;
15
19
  renderMode: typeof REACT_SERVER_RENDER_MODE;
16
- constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, }: {
20
+ constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, streamingTimeout, deferredActions, }: {
17
21
  context: any;
18
22
  customRender: any;
19
23
  extendRender: any;
@@ -22,10 +26,13 @@ export declare class ReactRenderServer {
22
26
  logger: any;
23
27
  responseTaskManager: any;
24
28
  responseStream: any;
29
+ streamingTimeout: any;
30
+ deferredActions: any;
25
31
  });
26
32
  render({ extractor, stats, }: {
27
33
  extractor: ChunkExtractor;
28
34
  stats: WebpackStats;
29
35
  }): Promise<string>;
30
36
  }
37
+ export {};
31
38
  //# sourceMappingURL=ReactRenderServer.d.ts.map
@@ -1,10 +1,9 @@
1
1
  import { Writable } from 'stream';
2
+ import { AbortedDeferredError, AbortedStreamError } from '@tinkoff/errors';
2
3
  import each from '@tinkoff/utils/array/each';
3
4
  import { renderReact } from '../react/index.es.js';
4
5
  import { flushFiles } from './blocks/utils/flushFiles.es.js';
5
6
 
6
- // @todo customize
7
- const RENDER_TIMEOUT = 30000;
8
7
  class HtmlWritable extends Writable {
9
8
  constructor({ responseTaskManager, responseStream, extractor, stats, }) {
10
9
  super();
@@ -66,7 +65,7 @@ const Deferred = () => {
66
65
  };
67
66
  class ReactRenderServer {
68
67
  // eslint-disable-next-line sort-class-members/sort-class-members
69
- constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, }) {
68
+ constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, streamingTimeout, deferredActions, }) {
70
69
  this.context = context;
71
70
  this.customRender = customRender;
72
71
  this.extendRender = extendRender;
@@ -75,6 +74,8 @@ class ReactRenderServer {
75
74
  this.log = logger('module-render');
76
75
  this.responseTaskManager = responseTaskManager;
77
76
  this.responseStream = responseStream;
77
+ this.streamingTimeout = streamingTimeout;
78
+ this.deferredActions = deferredActions;
78
79
  }
79
80
  render({ extractor, stats, }) {
80
81
  var _a;
@@ -110,6 +111,9 @@ class ReactRenderServer {
110
111
  log.info({
111
112
  event: 'streaming-render:start',
112
113
  });
114
+ const timeout = this.streamingTimeout;
115
+ const unfinishedActions = [];
116
+ let isAborted = false;
113
117
  const { pipe, abort } = renderToPipeableStream(renderResult, {
114
118
  // we need to run hydration only after first chunk is sent to client
115
119
  // https://github.com/reactwg/react-18/discussions/114
@@ -136,7 +140,12 @@ class ReactRenderServer {
136
140
  // so this is a best place to error logging
137
141
  log.error({
138
142
  event: 'streaming-render:error',
139
- error,
143
+ error: isAborted
144
+ ? new AbortedStreamError({
145
+ reason: `${timeout}ms timeout exceeded`,
146
+ unfinishedActions,
147
+ })
148
+ : error,
140
149
  });
141
150
  },
142
151
  onShellError(error) {
@@ -144,10 +153,24 @@ class ReactRenderServer {
144
153
  reject(error);
145
154
  },
146
155
  });
156
+ // global response stream timeo
147
157
  setTimeout(() => {
158
+ isAborted = true;
159
+ // abort unfinished deferred actions
160
+ this.deferredActions.forEach((action, name) => {
161
+ if (action.isRejected() || action.isResolved()) {
162
+ return;
163
+ }
164
+ unfinishedActions.push(name);
165
+ action.reject(new AbortedDeferredError());
166
+ });
167
+ // abort render stream
148
168
  abort();
149
- reject(new Error('React renderToPipeableStream timeout exceeded'));
150
- }, RENDER_TIMEOUT);
169
+ reject(new AbortedStreamError({
170
+ reason: `${timeout}ms timeout exceeded`,
171
+ unfinishedActions,
172
+ }));
173
+ }, timeout);
151
174
  });
152
175
  }
153
176
  const { renderToString } = require('react-dom/server');
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var stream = require('stream');
6
+ var errors = require('@tinkoff/errors');
6
7
  var each = require('@tinkoff/utils/array/each');
7
8
  var index = require('../react/index.js');
8
9
  var flushFiles = require('./blocks/utils/flushFiles.js');
@@ -11,8 +12,6 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
11
12
 
12
13
  var each__default = /*#__PURE__*/_interopDefaultLegacy(each);
13
14
 
14
- // @todo customize
15
- const RENDER_TIMEOUT = 30000;
16
15
  class HtmlWritable extends stream.Writable {
17
16
  constructor({ responseTaskManager, responseStream, extractor, stats, }) {
18
17
  super();
@@ -74,7 +73,7 @@ const Deferred = () => {
74
73
  };
75
74
  class ReactRenderServer {
76
75
  // eslint-disable-next-line sort-class-members/sort-class-members
77
- constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, }) {
76
+ constructor({ context, customRender, extendRender, di, renderMode, logger, responseTaskManager, responseStream, streamingTimeout, deferredActions, }) {
78
77
  this.context = context;
79
78
  this.customRender = customRender;
80
79
  this.extendRender = extendRender;
@@ -83,6 +82,8 @@ class ReactRenderServer {
83
82
  this.log = logger('module-render');
84
83
  this.responseTaskManager = responseTaskManager;
85
84
  this.responseStream = responseStream;
85
+ this.streamingTimeout = streamingTimeout;
86
+ this.deferredActions = deferredActions;
86
87
  }
87
88
  render({ extractor, stats, }) {
88
89
  var _a;
@@ -118,6 +119,9 @@ class ReactRenderServer {
118
119
  log.info({
119
120
  event: 'streaming-render:start',
120
121
  });
122
+ const timeout = this.streamingTimeout;
123
+ const unfinishedActions = [];
124
+ let isAborted = false;
121
125
  const { pipe, abort } = renderToPipeableStream(renderResult, {
122
126
  // we need to run hydration only after first chunk is sent to client
123
127
  // https://github.com/reactwg/react-18/discussions/114
@@ -144,7 +148,12 @@ class ReactRenderServer {
144
148
  // so this is a best place to error logging
145
149
  log.error({
146
150
  event: 'streaming-render:error',
147
- error,
151
+ error: isAborted
152
+ ? new errors.AbortedStreamError({
153
+ reason: `${timeout}ms timeout exceeded`,
154
+ unfinishedActions,
155
+ })
156
+ : error,
148
157
  });
149
158
  },
150
159
  onShellError(error) {
@@ -152,10 +161,24 @@ class ReactRenderServer {
152
161
  reject(error);
153
162
  },
154
163
  });
164
+ // global response stream timeo
155
165
  setTimeout(() => {
166
+ isAborted = true;
167
+ // abort unfinished deferred actions
168
+ this.deferredActions.forEach((action, name) => {
169
+ if (action.isRejected() || action.isResolved()) {
170
+ return;
171
+ }
172
+ unfinishedActions.push(name);
173
+ action.reject(new errors.AbortedDeferredError());
174
+ });
175
+ // abort render stream
156
176
  abort();
157
- reject(new Error('React renderToPipeableStream timeout exceeded'));
158
- }, RENDER_TIMEOUT);
177
+ reject(new errors.AbortedStreamError({
178
+ reason: `${timeout}ms timeout exceeded`,
179
+ unfinishedActions,
180
+ }));
181
+ }, timeout);
159
182
  });
160
183
  }
161
184
  const { renderToString } = require('react-dom/server');
package/lib/server.es.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
3
- import { CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
3
+ import { CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN, DEFERRED_ACTIONS_MAP_TOKEN } from '@tramvai/tokens-common';
4
4
  import { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
5
5
  import { ClientHintsModule, USER_AGENT_TOKEN } from '@tramvai/module-client-hints';
6
- import { RESOURCES_REGISTRY, RESOURCE_INLINE_OPTIONS, BACK_FORWARD_CACHE_ENABLED, RENDER_SLOTS, POLYFILL_CONDITION, HTML_ATTRS, MODERN_SATISFIES_TOKEN, RENDER_FLOW_AFTER_TOKEN, FETCH_WEBPACK_STATS_TOKEN, REACT_SERVER_RENDER_MODE, CUSTOM_RENDER, EXTEND_RENDER, ResourceType } from '@tramvai/tokens-render';
6
+ import { RESOURCES_REGISTRY, RESOURCE_INLINE_OPTIONS, BACK_FORWARD_CACHE_ENABLED, RENDER_SLOTS, POLYFILL_CONDITION, HTML_ATTRS, MODERN_SATISFIES_TOKEN, RENDER_FLOW_AFTER_TOKEN, FETCH_WEBPACK_STATS_TOKEN, REACT_SERVER_RENDER_MODE, CUSTOM_RENDER, EXTEND_RENDER, REACT_STREAMING_RENDER_TIMEOUT, ResourceType } from '@tramvai/tokens-render';
7
7
  export * from '@tramvai/tokens-render';
8
8
  import { Scope, optional } from '@tinkoff/dippy';
9
9
  import { satisfies } from '@tinkoff/user-agent';
@@ -213,6 +213,8 @@ Page Error Boundary will be rendered for the client`,
213
213
  logger: LOGGER_TOKEN,
214
214
  responseTaskManager: SERVER_RESPONSE_TASK_MANAGER,
215
215
  responseStream: SERVER_RESPONSE_STREAM,
216
+ streamingTimeout: REACT_STREAMING_RENDER_TIMEOUT,
217
+ deferredActions: DEFERRED_ACTIONS_MAP_TOKEN,
216
218
  },
217
219
  }),
218
220
  provide({
@@ -280,9 +282,8 @@ Page Error Boundary will be rendered for the client`,
280
282
  provide({
281
283
  provide: 'modernSatisfiesMemoryCache',
282
284
  scope: Scope.SINGLETON,
283
- // @todo - use larger `max` option and `memory-lfu` type after successful TCORE-4668 experiment
284
285
  useFactory: ({ createCache }) => {
285
- return createCache('memory', { max: 50 });
286
+ return createCache('memory', { max: 200 });
286
287
  },
287
288
  deps: {
288
289
  createCache: CREATE_CACHE_TOKEN,
@@ -300,6 +301,10 @@ Page Error Boundary will be rendered for the client`,
300
301
  provide: REACT_SERVER_RENDER_MODE,
301
302
  useValue: 'sync',
302
303
  }),
304
+ provide({
305
+ provide: REACT_STREAMING_RENDER_TIMEOUT,
306
+ useValue: 5000,
307
+ }),
303
308
  ],
304
309
  })
305
310
  ], RenderModule);
package/lib/server.js CHANGED
@@ -214,6 +214,8 @@ Page Error Boundary will be rendered for the client`,
214
214
  logger: tokensCommon.LOGGER_TOKEN,
215
215
  responseTaskManager: tokensServerPrivate.SERVER_RESPONSE_TASK_MANAGER,
216
216
  responseStream: tokensServerPrivate.SERVER_RESPONSE_STREAM,
217
+ streamingTimeout: tokensRender.REACT_STREAMING_RENDER_TIMEOUT,
218
+ deferredActions: tokensCommon.DEFERRED_ACTIONS_MAP_TOKEN,
217
219
  },
218
220
  }),
219
221
  core.provide({
@@ -281,9 +283,8 @@ Page Error Boundary will be rendered for the client`,
281
283
  core.provide({
282
284
  provide: 'modernSatisfiesMemoryCache',
283
285
  scope: dippy.Scope.SINGLETON,
284
- // @todo - use larger `max` option and `memory-lfu` type after successful TCORE-4668 experiment
285
286
  useFactory: ({ createCache }) => {
286
- return createCache('memory', { max: 50 });
287
+ return createCache('memory', { max: 200 });
287
288
  },
288
289
  deps: {
289
290
  createCache: tokensCommon.CREATE_CACHE_TOKEN,
@@ -301,6 +302,10 @@ Page Error Boundary will be rendered for the client`,
301
302
  provide: tokensRender.REACT_SERVER_RENDER_MODE,
302
303
  useValue: 'sync',
303
304
  }),
305
+ core.provide({
306
+ provide: tokensRender.REACT_STREAMING_RENDER_TIMEOUT,
307
+ useValue: 5000,
308
+ }),
304
309
  ],
305
310
  })
306
311
  ], exports.RenderModule);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-render",
3
- "version": "3.17.0",
3
+ "version": "3.19.0",
4
4
  "description": "",
5
5
  "browser": "lib/browser.js",
6
6
  "main": "lib/server.js",
@@ -24,15 +24,15 @@
24
24
  "@loadable/server": "^5.15.0",
25
25
  "@tinkoff/htmlpagebuilder": "0.6.1",
26
26
  "@tinkoff/layout-factory": "0.4.1",
27
- "@tinkoff/errors": "0.4.1",
27
+ "@tinkoff/errors": "0.4.2",
28
28
  "@tinkoff/url": "0.9.2",
29
- "@tinkoff/user-agent": "0.5.43",
30
- "@tramvai/module-client-hints": "3.17.0",
31
- "@tramvai/module-router": "3.17.0",
32
- "@tramvai/react": "3.17.0",
29
+ "@tinkoff/user-agent": "0.5.46",
30
+ "@tramvai/module-client-hints": "3.19.0",
31
+ "@tramvai/module-router": "3.19.0",
32
+ "@tramvai/react": "3.19.0",
33
33
  "@tramvai/safe-strings": "0.6.2",
34
- "@tramvai/tokens-render": "3.17.0",
35
- "@tramvai/experiments": "3.17.0",
34
+ "@tramvai/tokens-render": "3.19.0",
35
+ "@tramvai/experiments": "3.19.0",
36
36
  "@types/loadable__server": "^5.12.6",
37
37
  "node-fetch": "^2.6.1"
38
38
  },
@@ -40,14 +40,14 @@
40
40
  "@tinkoff/dippy": "0.9.1",
41
41
  "@tinkoff/utils": "^2.1.2",
42
42
  "@tinkoff/react-hooks": "0.2.1",
43
- "@tramvai/cli": "3.17.0",
44
- "@tramvai/core": "3.17.0",
45
- "@tramvai/module-common": "3.17.0",
46
- "@tramvai/state": "3.17.0",
47
- "@tramvai/test-helpers": "3.17.0",
48
- "@tramvai/tokens-common": "3.17.0",
49
- "@tramvai/tokens-router": "3.17.0",
50
- "@tramvai/tokens-server-private": "3.17.0",
43
+ "@tramvai/cli": "3.19.0",
44
+ "@tramvai/core": "3.19.0",
45
+ "@tramvai/module-common": "3.19.0",
46
+ "@tramvai/state": "3.19.0",
47
+ "@tramvai/test-helpers": "3.19.0",
48
+ "@tramvai/tokens-common": "3.19.0",
49
+ "@tramvai/tokens-router": "3.19.0",
50
+ "@tramvai/tokens-server-private": "3.19.0",
51
51
  "express": "^4.17.1",
52
52
  "prop-types": "^15.6.2",
53
53
  "react": ">=16.14.0",