@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 +60 -67
- package/lib/server.js +60 -67
- package/package.json +16 -15
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
|
-
//
|
|
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({
|
|
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({
|
|
100
|
-
|
|
101
|
-
|
|
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.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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:
|
|
119
|
-
|
|
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
|
-
//
|
|
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({
|
|
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({
|
|
101
|
-
|
|
102
|
-
|
|
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.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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:
|
|
120
|
-
|
|
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.
|
|
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.
|
|
29
|
-
"@tramvai/module-client-hints": "2.
|
|
30
|
-
"@tramvai/module-router": "2.
|
|
31
|
-
"@tramvai/react": "2.
|
|
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.
|
|
34
|
-
"@tramvai/experiments": "2.
|
|
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.
|
|
43
|
-
"@tramvai/core": "2.
|
|
44
|
-
"@tramvai/module-common": "2.
|
|
45
|
-
"@tramvai/state": "2.
|
|
46
|
-
"@tramvai/test-helpers": "2.
|
|
47
|
-
"@tramvai/tokens-common": "2.
|
|
48
|
-
"@tramvai/tokens-router": "2.
|
|
49
|
-
"@tramvai/tokens-server-private": "2.
|
|
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",
|