@tramvai/module-page-render-mode 7.11.0 → 7.16.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/browser.js +1 -1
- package/lib/private-tokens.d.ts +2 -1
- package/lib/server.es.js +1 -1
- package/lib/server.js +1 -0
- package/lib/staticPages/backgroundFetchService.d.ts +2 -1
- package/lib/staticPages/backgroundFetchService.es.js +4 -1
- package/lib/staticPages/backgroundFetchService.js +4 -1
- package/lib/staticPages/fileSystemCache.es.js +16 -6
- package/lib/staticPages/fileSystemCache.js +16 -6
- package/lib/staticPages/staticPagesService.es.js +30 -1
- package/lib/staticPages/staticPagesService.js +30 -1
- package/lib/staticPages.d.ts +46 -3
- package/lib/staticPages.es.js +77 -12
- package/lib/staticPages.js +79 -10
- package/lib/tokens.browser.js +4 -1
- package/lib/tokens.d.ts +13 -1
- package/lib/tokens.es.js +4 -1
- package/lib/tokens.js +4 -0
- package/lib/utils/getPageRenderMode.browser.js +2 -2
- package/lib/utils/getPageRenderMode.d.ts +3 -1
- package/lib/utils/getPageRenderMode.es.js +2 -2
- package/lib/utils/getPageRenderMode.js +2 -2
- package/package.json +15 -15
package/lib/browser.js
CHANGED
|
@@ -6,7 +6,7 @@ import { sharedProviders } from './shared.browser.js';
|
|
|
6
6
|
import { STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE } from './private-tokens.browser.js';
|
|
7
7
|
import { getPageRenderMode } from './utils/getPageRenderMode.browser.js';
|
|
8
8
|
import { PAGE_RENDER_DEFAULT_MODE } from './tokens.browser.js';
|
|
9
|
-
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE } from './tokens.browser.js';
|
|
9
|
+
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_ROUTE_TREE, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE } from './tokens.browser.js';
|
|
10
10
|
|
|
11
11
|
// @todo: перенести в @tramvai/module-render
|
|
12
12
|
const PageRenderModeModule = declareModule({
|
package/lib/private-tokens.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Counter, Gauge } from 'prom-client';
|
|
2
2
|
import { TramvaiRenderMode } from '@tramvai/tokens-render';
|
|
3
|
+
import { Route } from '@tinkoff/router';
|
|
3
4
|
import { BackgroundFetchService } from './staticPages/backgroundFetchService';
|
|
4
5
|
import { FileSystemCache } from './staticPages/fileSystemCache';
|
|
5
6
|
export declare const STATIC_PAGES_BACKGROUND_FETCH_SERVICE: BackgroundFetchService & {
|
|
@@ -21,7 +22,7 @@ export declare const STATIC_PAGES_FS_CACHE_METRICS_TOKEN: {
|
|
|
21
22
|
} & {
|
|
22
23
|
__type?: "base token" | undefined;
|
|
23
24
|
};
|
|
24
|
-
export declare const STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE: (() => TramvaiRenderMode) & {
|
|
25
|
+
export declare const STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE: ((route?: Route) => TramvaiRenderMode) & {
|
|
25
26
|
__type?: "base token" | undefined;
|
|
26
27
|
};
|
|
27
28
|
//# sourceMappingURL=private-tokens.d.ts.map
|
package/lib/server.es.js
CHANGED
|
@@ -2,7 +2,7 @@ import { declareModule } from '@tramvai/core';
|
|
|
2
2
|
import { ForceCSRModule } from './ForceCSRModule.es.js';
|
|
3
3
|
import { sharedProviders } from './shared.es.js';
|
|
4
4
|
import { staticPagesProviders } from './staticPages.es.js';
|
|
5
|
-
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE } from './tokens.es.js';
|
|
5
|
+
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_ROUTE_TREE, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE } from './tokens.es.js';
|
|
6
6
|
|
|
7
7
|
// @todo: перенести в @tramvai/module-render
|
|
8
8
|
const PageRenderModeModule = declareModule({
|
package/lib/server.js
CHANGED
|
@@ -29,6 +29,7 @@ exports.STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN = tokens.STATIC_PAGES_FS_CACHE_OPTIO
|
|
|
29
29
|
exports.STATIC_PAGES_KEY_TOKEN = tokens.STATIC_PAGES_KEY_TOKEN;
|
|
30
30
|
exports.STATIC_PAGES_MODIFY_CACHE = tokens.STATIC_PAGES_MODIFY_CACHE;
|
|
31
31
|
exports.STATIC_PAGES_OPTIONS_TOKEN = tokens.STATIC_PAGES_OPTIONS_TOKEN;
|
|
32
|
+
exports.STATIC_PAGES_ROUTE_TREE = tokens.STATIC_PAGES_ROUTE_TREE;
|
|
32
33
|
exports.STATIC_PAGES_SERVICE = tokens.STATIC_PAGES_SERVICE;
|
|
33
34
|
exports.STATIC_PAGES_SHOULD_USE_CACHE = tokens.STATIC_PAGES_SHOULD_USE_CACHE;
|
|
34
35
|
exports.PageRenderModeModule = PageRenderModeModule;
|
|
@@ -15,10 +15,11 @@ export declare class BackgroundFetchService {
|
|
|
15
15
|
backgroundFetchEnabled: BackgroundCacheEnabled;
|
|
16
16
|
});
|
|
17
17
|
enabled(): boolean;
|
|
18
|
-
revalidate({ cacheKey, pathname, headers, }: {
|
|
18
|
+
revalidate({ cacheKey, pathname, headers, query, }: {
|
|
19
19
|
cacheKey: string;
|
|
20
20
|
pathname: string;
|
|
21
21
|
headers: Record<string, string | string[] | undefined>;
|
|
22
|
+
query?: Record<string, string | string[]>;
|
|
22
23
|
}): Promise<void | {
|
|
23
24
|
body: string;
|
|
24
25
|
headers: {
|
|
@@ -15,14 +15,16 @@ class BackgroundFetchService {
|
|
|
15
15
|
enabled() {
|
|
16
16
|
return this.backgroundFetchEnabled();
|
|
17
17
|
}
|
|
18
|
-
async revalidate({ cacheKey, pathname, headers, }) {
|
|
18
|
+
async revalidate({ cacheKey, pathname, headers, query, }) {
|
|
19
19
|
if (this.requests.has(cacheKey)) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
+
// TODO: support --https
|
|
22
23
|
const revalidateUrl = format({
|
|
23
24
|
hostname: this.hostname,
|
|
24
25
|
port: this.port,
|
|
25
26
|
path: pathname,
|
|
27
|
+
query,
|
|
26
28
|
});
|
|
27
29
|
this.requests.add(cacheKey);
|
|
28
30
|
this.log.debug({
|
|
@@ -34,6 +36,7 @@ class BackgroundFetchService {
|
|
|
34
36
|
headers: {
|
|
35
37
|
...headers,
|
|
36
38
|
'X-Tramvai-Static-Page-Revalidate': 'true',
|
|
39
|
+
'X-Tramvai-Service-Name': 'BACKGROUND_STATIC_PAGE_REVALIDATE',
|
|
37
40
|
},
|
|
38
41
|
signal: AbortSignal.timeout(10000),
|
|
39
42
|
// we need to save raw redirect response status code and headers
|
|
@@ -19,14 +19,16 @@ class BackgroundFetchService {
|
|
|
19
19
|
enabled() {
|
|
20
20
|
return this.backgroundFetchEnabled();
|
|
21
21
|
}
|
|
22
|
-
async revalidate({ cacheKey, pathname, headers, }) {
|
|
22
|
+
async revalidate({ cacheKey, pathname, headers, query, }) {
|
|
23
23
|
if (this.requests.has(cacheKey)) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
+
// TODO: support --https
|
|
26
27
|
const revalidateUrl = url.format({
|
|
27
28
|
hostname: this.hostname,
|
|
28
29
|
port: this.port,
|
|
29
30
|
path: pathname,
|
|
31
|
+
query,
|
|
30
32
|
});
|
|
31
33
|
this.requests.add(cacheKey);
|
|
32
34
|
this.log.debug({
|
|
@@ -38,6 +40,7 @@ class BackgroundFetchService {
|
|
|
38
40
|
headers: {
|
|
39
41
|
...headers,
|
|
40
42
|
'X-Tramvai-Static-Page-Revalidate': 'true',
|
|
43
|
+
'X-Tramvai-Service-Name': 'BACKGROUND_STATIC_PAGE_REVALIDATE',
|
|
41
44
|
},
|
|
42
45
|
signal: AbortSignal.timeout(10000),
|
|
43
46
|
// we need to save raw redirect response status code and headers
|
|
@@ -18,7 +18,7 @@ class FileSystemCache {
|
|
|
18
18
|
metrics;
|
|
19
19
|
limit = pLimit(8);
|
|
20
20
|
constructor({ directory, maxSize, ttl, allowStale, logger, metrics, }) {
|
|
21
|
-
this.directory = directory;
|
|
21
|
+
this.directory = path.isAbsolute(directory) ? directory : path.join(process.cwd(), directory);
|
|
22
22
|
this.maxSize = maxSize;
|
|
23
23
|
this.ttl = ttl;
|
|
24
24
|
this.allowStale = allowStale;
|
|
@@ -35,6 +35,8 @@ class FileSystemCache {
|
|
|
35
35
|
directory: this.directory,
|
|
36
36
|
});
|
|
37
37
|
try {
|
|
38
|
+
// fill zero values for metrics
|
|
39
|
+
this.updateMetrics();
|
|
38
40
|
await this.scanDirectory();
|
|
39
41
|
this.log.info({
|
|
40
42
|
event: 'init-complete',
|
|
@@ -44,11 +46,19 @@ class FileSystemCache {
|
|
|
44
46
|
this.updateMetrics();
|
|
45
47
|
}
|
|
46
48
|
catch (error) {
|
|
47
|
-
if (
|
|
48
|
-
//
|
|
49
|
-
await promises.mkdir(this.directory, { recursive: true })
|
|
49
|
+
if (error?.code === 'ENOENT') {
|
|
50
|
+
// static directory path can be missing on first run, so always create it
|
|
51
|
+
await promises.mkdir(this.directory, { recursive: true }).catch((mkdirError) => {
|
|
52
|
+
this.log.warn({
|
|
53
|
+
event: 'mkdir-error',
|
|
54
|
+
message: `Failed to create static cache directory ${this.directory}, maybe read-only file system is used.
|
|
55
|
+
In k8s environment, it means that "readOnlyRootFilesystem" option is enabled for deployment, to fix this issue, add 'dist' directory as a emptyDir volume mount.`,
|
|
56
|
+
error: mkdirError,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
50
59
|
}
|
|
51
|
-
|
|
60
|
+
// static directory path includes unique build id in development mode, so warn only in production
|
|
61
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
52
62
|
this.log.warn({
|
|
53
63
|
event: 'init-error',
|
|
54
64
|
message: error?.code === 'ENOENT'
|
|
@@ -253,7 +263,7 @@ and copy "dist/static" folder into the Docker image, to ensure the pages cache i
|
|
|
253
263
|
}
|
|
254
264
|
catch (error) {
|
|
255
265
|
if (process.env.NODE_ENV !== 'development') {
|
|
256
|
-
this.log.
|
|
266
|
+
this.log.info({
|
|
257
267
|
event: 'meta-read-error',
|
|
258
268
|
error: error,
|
|
259
269
|
});
|
|
@@ -27,7 +27,7 @@ class FileSystemCache {
|
|
|
27
27
|
metrics;
|
|
28
28
|
limit = pLimit__default["default"](8);
|
|
29
29
|
constructor({ directory, maxSize, ttl, allowStale, logger, metrics, }) {
|
|
30
|
-
this.directory = directory;
|
|
30
|
+
this.directory = path__default["default"].isAbsolute(directory) ? directory : path__default["default"].join(process.cwd(), directory);
|
|
31
31
|
this.maxSize = maxSize;
|
|
32
32
|
this.ttl = ttl;
|
|
33
33
|
this.allowStale = allowStale;
|
|
@@ -44,6 +44,8 @@ class FileSystemCache {
|
|
|
44
44
|
directory: this.directory,
|
|
45
45
|
});
|
|
46
46
|
try {
|
|
47
|
+
// fill zero values for metrics
|
|
48
|
+
this.updateMetrics();
|
|
47
49
|
await this.scanDirectory();
|
|
48
50
|
this.log.info({
|
|
49
51
|
event: 'init-complete',
|
|
@@ -53,11 +55,19 @@ class FileSystemCache {
|
|
|
53
55
|
this.updateMetrics();
|
|
54
56
|
}
|
|
55
57
|
catch (error) {
|
|
56
|
-
if (
|
|
57
|
-
//
|
|
58
|
-
await node_fs.promises.mkdir(this.directory, { recursive: true })
|
|
58
|
+
if (error?.code === 'ENOENT') {
|
|
59
|
+
// static directory path can be missing on first run, so always create it
|
|
60
|
+
await node_fs.promises.mkdir(this.directory, { recursive: true }).catch((mkdirError) => {
|
|
61
|
+
this.log.warn({
|
|
62
|
+
event: 'mkdir-error',
|
|
63
|
+
message: `Failed to create static cache directory ${this.directory}, maybe read-only file system is used.
|
|
64
|
+
In k8s environment, it means that "readOnlyRootFilesystem" option is enabled for deployment, to fix this issue, add 'dist' directory as a emptyDir volume mount.`,
|
|
65
|
+
error: mkdirError,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
59
68
|
}
|
|
60
|
-
|
|
69
|
+
// static directory path includes unique build id in development mode, so warn only in production
|
|
70
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
61
71
|
this.log.warn({
|
|
62
72
|
event: 'init-error',
|
|
63
73
|
message: error?.code === 'ENOENT'
|
|
@@ -262,7 +272,7 @@ and copy "dist/static" folder into the Docker image, to ensure the pages cache i
|
|
|
262
272
|
}
|
|
263
273
|
catch (error) {
|
|
264
274
|
if (process.env.NODE_ENV !== 'development') {
|
|
265
|
-
this.log.
|
|
275
|
+
this.log.info({
|
|
266
276
|
event: 'meta-read-error',
|
|
267
277
|
error: error,
|
|
268
278
|
});
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { getCacheKey } from '../utils/cacheKey.es.js';
|
|
2
2
|
|
|
3
3
|
// `Location` is required for 3xx responses
|
|
4
|
-
|
|
4
|
+
// `X-Original-Host` and `Host` are required for RequestManager and Router
|
|
5
|
+
const DEFAULT_HEADERS_WHITELIST = [
|
|
6
|
+
'Location',
|
|
7
|
+
'Content-Type',
|
|
8
|
+
'Content-Length',
|
|
9
|
+
'X-App-Id',
|
|
10
|
+
'X-Original-Host',
|
|
11
|
+
'Host',
|
|
12
|
+
'X-Forwarded-Host',
|
|
13
|
+
'X-Forwarded-Proto',
|
|
14
|
+
];
|
|
5
15
|
class StaticPagesService {
|
|
6
16
|
key;
|
|
7
17
|
pathname;
|
|
@@ -39,6 +49,7 @@ class StaticPagesService {
|
|
|
39
49
|
this.cache5xxResponse = cache5xxResponse;
|
|
40
50
|
this.cacheControlFactory = cacheControlFactory;
|
|
41
51
|
}
|
|
52
|
+
// eslint-disable-next-line max-statements
|
|
42
53
|
async respond(onSuccess) {
|
|
43
54
|
if (!this.hasCache()) {
|
|
44
55
|
this.log.debug({
|
|
@@ -134,7 +145,9 @@ class StaticPagesService {
|
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
147
|
const incomingHeaders = this.requestManager.getHeaders();
|
|
148
|
+
const incomingQuery = this.requestManager.getParsedUrl().query;
|
|
137
149
|
const revalidateHeaders = {};
|
|
150
|
+
const revalidateQuery = {};
|
|
138
151
|
this.options.allowedHeaders.concat(DEFAULT_HEADERS_WHITELIST).forEach((header) => {
|
|
139
152
|
const lowercaseHeader = header.toLowerCase();
|
|
140
153
|
if (incomingHeaders[header]) {
|
|
@@ -144,11 +157,17 @@ class StaticPagesService {
|
|
|
144
157
|
revalidateHeaders[lowercaseHeader] = incomingHeaders[lowercaseHeader];
|
|
145
158
|
}
|
|
146
159
|
});
|
|
160
|
+
this.options.allowedQuery.forEach((query) => {
|
|
161
|
+
if (incomingQuery[query]) {
|
|
162
|
+
revalidateQuery[query] = incomingQuery[query];
|
|
163
|
+
}
|
|
164
|
+
});
|
|
147
165
|
await this.backgroundFetchService
|
|
148
166
|
.revalidate({
|
|
149
167
|
cacheKey: this.cacheKey,
|
|
150
168
|
pathname: this.pathname,
|
|
151
169
|
headers: revalidateHeaders,
|
|
170
|
+
query: revalidateQuery,
|
|
152
171
|
})
|
|
153
172
|
.then((response) => {
|
|
154
173
|
if (!response) {
|
|
@@ -161,6 +180,16 @@ class StaticPagesService {
|
|
|
161
180
|
});
|
|
162
181
|
return;
|
|
163
182
|
}
|
|
183
|
+
// for 4xx responses we want to cache only HTML pages
|
|
184
|
+
if (response.status >= 400 &&
|
|
185
|
+
response.status < 500 &&
|
|
186
|
+
!response.headers['content-type']?.includes('text/html')) {
|
|
187
|
+
this.log.debug({
|
|
188
|
+
event: 'cache-set-4xx',
|
|
189
|
+
cacheKey: this.cacheKey,
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
164
193
|
const cachedHeaders = {};
|
|
165
194
|
const responseHeaders = response.headers;
|
|
166
195
|
this.options.allowedHeaders.concat(DEFAULT_HEADERS_WHITELIST).forEach((header) => {
|
|
@@ -5,7 +5,17 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var cacheKey = require('../utils/cacheKey.js');
|
|
6
6
|
|
|
7
7
|
// `Location` is required for 3xx responses
|
|
8
|
-
|
|
8
|
+
// `X-Original-Host` and `Host` are required for RequestManager and Router
|
|
9
|
+
const DEFAULT_HEADERS_WHITELIST = [
|
|
10
|
+
'Location',
|
|
11
|
+
'Content-Type',
|
|
12
|
+
'Content-Length',
|
|
13
|
+
'X-App-Id',
|
|
14
|
+
'X-Original-Host',
|
|
15
|
+
'Host',
|
|
16
|
+
'X-Forwarded-Host',
|
|
17
|
+
'X-Forwarded-Proto',
|
|
18
|
+
];
|
|
9
19
|
class StaticPagesService {
|
|
10
20
|
key;
|
|
11
21
|
pathname;
|
|
@@ -43,6 +53,7 @@ class StaticPagesService {
|
|
|
43
53
|
this.cache5xxResponse = cache5xxResponse;
|
|
44
54
|
this.cacheControlFactory = cacheControlFactory;
|
|
45
55
|
}
|
|
56
|
+
// eslint-disable-next-line max-statements
|
|
46
57
|
async respond(onSuccess) {
|
|
47
58
|
if (!this.hasCache()) {
|
|
48
59
|
this.log.debug({
|
|
@@ -138,7 +149,9 @@ class StaticPagesService {
|
|
|
138
149
|
}
|
|
139
150
|
}
|
|
140
151
|
const incomingHeaders = this.requestManager.getHeaders();
|
|
152
|
+
const incomingQuery = this.requestManager.getParsedUrl().query;
|
|
141
153
|
const revalidateHeaders = {};
|
|
154
|
+
const revalidateQuery = {};
|
|
142
155
|
this.options.allowedHeaders.concat(DEFAULT_HEADERS_WHITELIST).forEach((header) => {
|
|
143
156
|
const lowercaseHeader = header.toLowerCase();
|
|
144
157
|
if (incomingHeaders[header]) {
|
|
@@ -148,11 +161,17 @@ class StaticPagesService {
|
|
|
148
161
|
revalidateHeaders[lowercaseHeader] = incomingHeaders[lowercaseHeader];
|
|
149
162
|
}
|
|
150
163
|
});
|
|
164
|
+
this.options.allowedQuery.forEach((query) => {
|
|
165
|
+
if (incomingQuery[query]) {
|
|
166
|
+
revalidateQuery[query] = incomingQuery[query];
|
|
167
|
+
}
|
|
168
|
+
});
|
|
151
169
|
await this.backgroundFetchService
|
|
152
170
|
.revalidate({
|
|
153
171
|
cacheKey: this.cacheKey,
|
|
154
172
|
pathname: this.pathname,
|
|
155
173
|
headers: revalidateHeaders,
|
|
174
|
+
query: revalidateQuery,
|
|
156
175
|
})
|
|
157
176
|
.then((response) => {
|
|
158
177
|
if (!response) {
|
|
@@ -165,6 +184,16 @@ class StaticPagesService {
|
|
|
165
184
|
});
|
|
166
185
|
return;
|
|
167
186
|
}
|
|
187
|
+
// for 4xx responses we want to cache only HTML pages
|
|
188
|
+
if (response.status >= 400 &&
|
|
189
|
+
response.status < 500 &&
|
|
190
|
+
!response.headers['content-type']?.includes('text/html')) {
|
|
191
|
+
this.log.debug({
|
|
192
|
+
event: 'cache-set-4xx',
|
|
193
|
+
cacheKey: this.cacheKey,
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
168
197
|
const cachedHeaders = {};
|
|
169
198
|
const responseHeaders = response.headers;
|
|
170
199
|
this.options.allowedHeaders.concat(DEFAULT_HEADERS_WHITELIST).forEach((header) => {
|
package/lib/staticPages.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
+
import { Route, RouteTree } from '@tinkoff/router';
|
|
2
3
|
import { BackgroundFetchService } from './staticPages/backgroundFetchService';
|
|
3
4
|
import { StaticPagesService } from './staticPages/staticPagesService';
|
|
4
5
|
import { FileSystemCache } from './staticPages/fileSystemCache';
|
|
5
|
-
export declare const staticPagesProviders: (import("@tinkoff/dippy/lib/Provider").ValueProvider<{
|
|
6
|
+
export declare const staticPagesProviders: (import("@tinkoff/dippy/lib/Provider").ValueProvider<((route?: Route | undefined) => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
7
|
+
__type?: "base token" | undefined;
|
|
8
|
+
}> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithoutDeps<((route?: Route | undefined) => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
9
|
+
__type?: "base token" | undefined;
|
|
10
|
+
}> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithoutDeps<((route?: Route | undefined) => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
11
|
+
__type?: "base token" | undefined;
|
|
12
|
+
}> | import("@tinkoff/dippy/lib/Provider").ValueProvider<{
|
|
6
13
|
hit: import("prom-client").Counter<any>;
|
|
7
14
|
} & {
|
|
8
15
|
__type?: "base token" | undefined;
|
|
@@ -228,7 +235,7 @@ export declare const staticPagesProviders: (import("@tinkoff/dippy/lib/Provider"
|
|
|
228
235
|
}) | null;
|
|
229
236
|
optional: true;
|
|
230
237
|
};
|
|
231
|
-
}, (() => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
238
|
+
}, ((route?: Route | undefined) => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
232
239
|
__type?: "base token" | undefined;
|
|
233
240
|
}> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
|
|
234
241
|
requestManager: import("@tramvai/tokens-common").RequestManager & {
|
|
@@ -261,7 +268,7 @@ export declare const staticPagesProviders: (import("@tinkoff/dippy/lib/Provider"
|
|
|
261
268
|
}) | null;
|
|
262
269
|
optional: true;
|
|
263
270
|
};
|
|
264
|
-
}, (() => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
271
|
+
}, ((route?: Route | undefined) => import("@tramvai/tokens-render").TramvaiRenderMode) & {
|
|
265
272
|
__type?: "base token" | undefined;
|
|
266
273
|
}> | import("@tinkoff/dippy/lib/Provider").ValueProvider<BackgroundFetchService & {
|
|
267
274
|
__type?: "base token" | undefined;
|
|
@@ -531,5 +538,41 @@ export declare const staticPagesProviders: (import("@tinkoff/dippy/lib/Provider"
|
|
|
531
538
|
__papi_parameters__: import("@tramvai/papi").NormalizedPapiParameters<any, any>;
|
|
532
539
|
} & {
|
|
533
540
|
__type?: "multi token" | undefined;
|
|
541
|
+
}> | import("@tinkoff/dippy/lib/Provider").ValueProvider<RouteTree & {
|
|
542
|
+
__type?: "base token" | undefined;
|
|
543
|
+
}> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
|
|
544
|
+
di: import("@tinkoff/dippy").Container & {
|
|
545
|
+
__type?: "base token" | undefined;
|
|
546
|
+
};
|
|
547
|
+
}, RouteTree & {
|
|
548
|
+
__type?: "base token" | undefined;
|
|
549
|
+
}> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithoutDeps<RouteTree & {
|
|
550
|
+
__type?: "base token" | undefined;
|
|
551
|
+
}> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
|
|
552
|
+
di: import("@tinkoff/dippy").Container & {
|
|
553
|
+
__type?: "base token" | undefined;
|
|
554
|
+
};
|
|
555
|
+
}, RouteTree & {
|
|
556
|
+
__type?: "base token" | undefined;
|
|
557
|
+
}> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithoutDeps<RouteTree & {
|
|
558
|
+
__type?: "base token" | undefined;
|
|
559
|
+
}> | import("@tinkoff/dippy/lib/Provider").ClassProviderWithDeps<{
|
|
560
|
+
router: import("@tinkoff/router").AbstractRouter & {
|
|
561
|
+
__type?: "base token" | undefined;
|
|
562
|
+
};
|
|
563
|
+
routeTree: RouteTree & {
|
|
564
|
+
__type?: "base token" | undefined;
|
|
565
|
+
};
|
|
566
|
+
}, import("@tramvai/core").Command & {
|
|
567
|
+
__type?: "multi token" | undefined;
|
|
568
|
+
}> | import("@tinkoff/dippy/lib/Provider").FactoryProviderWithDeps<{
|
|
569
|
+
router: import("@tinkoff/router").AbstractRouter & {
|
|
570
|
+
__type?: "base token" | undefined;
|
|
571
|
+
};
|
|
572
|
+
routeTree: RouteTree & {
|
|
573
|
+
__type?: "base token" | undefined;
|
|
574
|
+
};
|
|
575
|
+
}, import("@tramvai/core").Command & {
|
|
576
|
+
__type?: "multi token" | undefined;
|
|
534
577
|
}>)[];
|
|
535
578
|
//# sourceMappingURL=staticPages.d.ts.map
|
package/lib/staticPages.es.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import flatten from '@tinkoff/utils/array/flatten';
|
|
1
2
|
import { provide, Scope, commandLineListTokens, optional, DI_TOKEN } from '@tramvai/core';
|
|
2
3
|
import { CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, ENV_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN } from '@tramvai/tokens-common';
|
|
3
4
|
import { FASTIFY_RESPONSE } from '@tramvai/tokens-server-private';
|
|
4
|
-
import { ROUTER_TOKEN, PAGE_SERVICE_TOKEN, LINK_PREFETCH_MANAGER_TOKEN, PAGE_REGISTRY_TOKEN } from '@tramvai/tokens-router';
|
|
5
|
+
import { ROUTER_TOKEN, PAGE_SERVICE_TOKEN, LINK_PREFETCH_MANAGER_TOKEN, PAGE_REGISTRY_TOKEN, ROUTES_TOKEN } from '@tramvai/tokens-router';
|
|
5
6
|
import { SERVER_MODULE_PAPI_PRIVATE_ROUTE } from '@tramvai/tokens-server';
|
|
6
7
|
import { METRICS_MODULE_TOKEN } from '@tramvai/tokens-metrics';
|
|
7
8
|
import { createPapiMethod } from '@tramvai/papi';
|
|
9
|
+
import { isWildcard, RouteTree } from '@tinkoff/router';
|
|
8
10
|
import { StopCommandLineRunnerError } from './error.es.js';
|
|
9
|
-
import { STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_SHOULD_USE_CACHE, PAGE_RENDER_DEFAULT_MODE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_SERVICE, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_COMMAND_LINE } from './tokens.es.js';
|
|
11
|
+
import { STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_SHOULD_USE_CACHE, PAGE_RENDER_DEFAULT_MODE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_SERVICE, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_ROUTE_TREE, STATIC_PAGES_COMMAND_LINE } from './tokens.es.js';
|
|
10
12
|
import { getPageRenderMode } from './utils/getPageRenderMode.es.js';
|
|
11
13
|
import { getCacheKey } from './utils/cacheKey.es.js';
|
|
12
14
|
import { BackgroundFetchService } from './staticPages/backgroundFetchService.es.js';
|
|
@@ -14,6 +16,11 @@ import { StaticPagesService } from './staticPages/staticPagesService.es.js';
|
|
|
14
16
|
import { FileSystemCache } from './staticPages/fileSystemCache.es.js';
|
|
15
17
|
import { STATIC_PAGES_CACHE_METRICS_TOKEN, STATIC_PAGES_FS_CACHE_METRICS_TOKEN, STATIC_PAGES_FS_CACHE_TOKEN, STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE, STATIC_PAGES_BACKGROUND_FETCH_SERVICE } from './private-tokens.es.js';
|
|
16
18
|
|
|
19
|
+
// small performance optimization to prevent stack trace generation for static StopCommandLineRunnerError
|
|
20
|
+
const { stackTraceLimit } = Error;
|
|
21
|
+
Error.stackTraceLimit = 0;
|
|
22
|
+
const STOP_COMMAND_LINE_ERROR = new StopCommandLineRunnerError();
|
|
23
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
17
24
|
const staticPagesProviders = [
|
|
18
25
|
provide({
|
|
19
26
|
provide: STATIC_PAGES_CACHE_METRICS_TOKEN,
|
|
@@ -48,11 +55,14 @@ const staticPagesProviders = [
|
|
|
48
55
|
provide({
|
|
49
56
|
provide: STATIC_PAGES_OPTIONS_TOKEN,
|
|
50
57
|
useValue: {
|
|
51
|
-
// @TODO: unique
|
|
58
|
+
// @TODO: unique cache parameters per pages
|
|
52
59
|
ttl: 5 * 60 * 1000,
|
|
53
60
|
maxSize: 100,
|
|
54
61
|
allowStale: true,
|
|
62
|
+
// @TODO: unique headers per pages
|
|
55
63
|
allowedHeaders: [],
|
|
64
|
+
// @TODO: unique query per pages
|
|
65
|
+
allowedQuery: [],
|
|
56
66
|
},
|
|
57
67
|
}),
|
|
58
68
|
provide({
|
|
@@ -170,9 +180,10 @@ const staticPagesProviders = [
|
|
|
170
180
|
provide({
|
|
171
181
|
provide: STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE,
|
|
172
182
|
useFactory: ({ requestManager, pageService, router, defaultRenderMode, fileSystemCache }) => {
|
|
173
|
-
return () => getPageRenderMode({
|
|
183
|
+
return (route) => getPageRenderMode({
|
|
174
184
|
pageService,
|
|
175
185
|
router,
|
|
186
|
+
route,
|
|
176
187
|
requestManager,
|
|
177
188
|
defaultRenderMode,
|
|
178
189
|
fileSystemCache,
|
|
@@ -240,12 +251,13 @@ const staticPagesProviders = [
|
|
|
240
251
|
provide: staticPagesCommandLine
|
|
241
252
|
? commandLineListTokens[staticPagesCommandLine]
|
|
242
253
|
: commandLineListTokens.customerStart,
|
|
243
|
-
useFactory: ({ staticPagesService, staticPagesCacheMetrics, logger, requestManager, responseManager, staticPagesKey, linkPrefetchManager, resolvePageRenderMode, router, pageRegistry, }) => {
|
|
254
|
+
useFactory: ({ staticPagesService, staticPagesCacheMetrics, logger, requestManager, responseManager, staticPagesKey, linkPrefetchManager, resolvePageRenderMode, router, pageRegistry, routeTree, }) => {
|
|
244
255
|
const log = logger('static-pages');
|
|
256
|
+
// eslint-disable-next-line max-statements
|
|
245
257
|
return async function staticPagesFromCache() {
|
|
246
258
|
const isPrerenderRequest = !!requestManager.getHeader('x-tramvai-prerender');
|
|
247
259
|
const { pathname } = requestManager.getParsedUrl();
|
|
248
|
-
|
|
260
|
+
let route = router?.getCurrentRoute() ?? router?.resolve(pathname);
|
|
249
261
|
// prefetch route only for `tramvai static` because it can be async and slow in runtime
|
|
250
262
|
if (isPrerenderRequest) {
|
|
251
263
|
log.debug(`Should prefetch route and component to determine if page is static: ${pathname}`);
|
|
@@ -260,8 +272,22 @@ const staticPagesProviders = [
|
|
|
260
272
|
}
|
|
261
273
|
});
|
|
262
274
|
}
|
|
263
|
-
|
|
275
|
+
// dynamic route will be resolved after prefetch
|
|
276
|
+
if (isPrerenderRequest && !route) {
|
|
277
|
+
route = router?.getCurrentRoute() ?? router?.resolve(pathname);
|
|
278
|
+
}
|
|
279
|
+
const isStatic = resolvePageRenderMode(route) === 'static';
|
|
264
280
|
const shouldUseCache = staticPagesService.shouldUseCache();
|
|
281
|
+
// we can't use cache for wildcard routes,
|
|
282
|
+
// because wildcard routes can match any possible urls
|
|
283
|
+
const isWildcardRoute = route
|
|
284
|
+
? isWildcard(route.path)
|
|
285
|
+
: routeTree.getWildcard(pathname);
|
|
286
|
+
// routes can be resolved in runtime with ROUTE_RESOLVE_TOKEN,
|
|
287
|
+
// but this routes exists only in Request scope, so we use STATIC_PAGES_ROUTE_TREE
|
|
288
|
+
// to save minimal info about resolved routes between requests, because
|
|
289
|
+
// we want to be sure that we use cache and revalidation only for resolved routes
|
|
290
|
+
const isUnknownRoute = !route && !routeTree.getRoute(pathname);
|
|
265
291
|
if (isStatic) {
|
|
266
292
|
responseManager.setHeader('X-Tramvai-Static-Page-Key', staticPagesKey());
|
|
267
293
|
// we need to tell for `tramvai static` prerendering command that this page has `static` render mode,
|
|
@@ -270,16 +296,23 @@ const staticPagesProviders = [
|
|
|
270
296
|
if (isPrerenderRequest && route) {
|
|
271
297
|
responseManager.setHeader('X-Tramvai-Static-Page-Route', JSON.stringify(route));
|
|
272
298
|
}
|
|
273
|
-
if (shouldUseCache) {
|
|
274
|
-
log.
|
|
299
|
+
if (shouldUseCache && !isWildcardRoute && !isUnknownRoute) {
|
|
300
|
+
log.info(`Should use static pages cache: ${pathname}`);
|
|
275
301
|
await staticPagesService.respond(() => {
|
|
276
|
-
log.
|
|
302
|
+
log.info(`Successful static page response from cache: ${pathname}`);
|
|
277
303
|
staticPagesCacheMetrics.hit.inc();
|
|
278
|
-
throw
|
|
304
|
+
throw STOP_COMMAND_LINE_ERROR;
|
|
279
305
|
});
|
|
280
306
|
}
|
|
281
307
|
else {
|
|
282
|
-
|
|
308
|
+
let msg = `Static pages cache is not used for this request: ${pathname}`;
|
|
309
|
+
if (shouldUseCache && isWildcardRoute) {
|
|
310
|
+
msg += `, because wildcard route was resolved`;
|
|
311
|
+
}
|
|
312
|
+
else if (shouldUseCache && isUnknownRoute) {
|
|
313
|
+
msg += `, because route for this url was never resolved`;
|
|
314
|
+
}
|
|
315
|
+
log.info(msg);
|
|
283
316
|
}
|
|
284
317
|
}
|
|
285
318
|
};
|
|
@@ -295,6 +328,7 @@ const staticPagesProviders = [
|
|
|
295
328
|
resolvePageRenderMode: STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE,
|
|
296
329
|
router: optional(ROUTER_TOKEN),
|
|
297
330
|
pageRegistry: optional(PAGE_REGISTRY_TOKEN),
|
|
331
|
+
routeTree: STATIC_PAGES_ROUTE_TREE,
|
|
298
332
|
},
|
|
299
333
|
});
|
|
300
334
|
};
|
|
@@ -367,6 +401,37 @@ const staticPagesProviders = [
|
|
|
367
401
|
logger: LOGGER_TOKEN,
|
|
368
402
|
},
|
|
369
403
|
}),
|
|
404
|
+
provide({
|
|
405
|
+
provide: STATIC_PAGES_ROUTE_TREE,
|
|
406
|
+
scope: Scope.SINGLETON,
|
|
407
|
+
useFactory: ({ di }) => {
|
|
408
|
+
const staticRoutes = di.get(optional(ROUTES_TOKEN)) ?? [];
|
|
409
|
+
return new RouteTree(flatten(staticRoutes).map((route) => ({ name: route.name, path: route.path })));
|
|
410
|
+
},
|
|
411
|
+
deps: {
|
|
412
|
+
di: DI_TOKEN,
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
provide({
|
|
416
|
+
provide: commandLineListTokens.clear,
|
|
417
|
+
useFactory: ({ router, routeTree }) => {
|
|
418
|
+
return () => {
|
|
419
|
+
const currentUrl = router.getCurrentUrl();
|
|
420
|
+
const currentRoute = router.getCurrentRoute();
|
|
421
|
+
// save dynamically resolved route in route tree to use it in next requests before route resolving
|
|
422
|
+
if (currentRoute && currentUrl && !routeTree.getRoute(currentUrl.pathname)) {
|
|
423
|
+
routeTree.addRoute({
|
|
424
|
+
name: currentRoute.name,
|
|
425
|
+
path: currentRoute.path,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
},
|
|
430
|
+
deps: {
|
|
431
|
+
router: ROUTER_TOKEN,
|
|
432
|
+
routeTree: STATIC_PAGES_ROUTE_TREE,
|
|
433
|
+
},
|
|
434
|
+
}),
|
|
370
435
|
];
|
|
371
436
|
|
|
372
437
|
export { staticPagesProviders };
|
package/lib/staticPages.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var flatten = require('@tinkoff/utils/array/flatten');
|
|
5
6
|
var core = require('@tramvai/core');
|
|
6
7
|
var tokensCommon = require('@tramvai/tokens-common');
|
|
7
8
|
var tokensServerPrivate = require('@tramvai/tokens-server-private');
|
|
@@ -9,6 +10,7 @@ var tokensRouter = require('@tramvai/tokens-router');
|
|
|
9
10
|
var tokensServer = require('@tramvai/tokens-server');
|
|
10
11
|
var tokensMetrics = require('@tramvai/tokens-metrics');
|
|
11
12
|
var papi = require('@tramvai/papi');
|
|
13
|
+
var router = require('@tinkoff/router');
|
|
12
14
|
var error = require('./error.js');
|
|
13
15
|
var tokens = require('./tokens.js');
|
|
14
16
|
var getPageRenderMode = require('./utils/getPageRenderMode.js');
|
|
@@ -18,6 +20,15 @@ var staticPagesService = require('./staticPages/staticPagesService.js');
|
|
|
18
20
|
var fileSystemCache = require('./staticPages/fileSystemCache.js');
|
|
19
21
|
var privateTokens = require('./private-tokens.js');
|
|
20
22
|
|
|
23
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
24
|
+
|
|
25
|
+
var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
|
|
26
|
+
|
|
27
|
+
// small performance optimization to prevent stack trace generation for static StopCommandLineRunnerError
|
|
28
|
+
const { stackTraceLimit } = Error;
|
|
29
|
+
Error.stackTraceLimit = 0;
|
|
30
|
+
const STOP_COMMAND_LINE_ERROR = new error.StopCommandLineRunnerError();
|
|
31
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
21
32
|
const staticPagesProviders = [
|
|
22
33
|
core.provide({
|
|
23
34
|
provide: privateTokens.STATIC_PAGES_CACHE_METRICS_TOKEN,
|
|
@@ -52,11 +63,14 @@ const staticPagesProviders = [
|
|
|
52
63
|
core.provide({
|
|
53
64
|
provide: tokens.STATIC_PAGES_OPTIONS_TOKEN,
|
|
54
65
|
useValue: {
|
|
55
|
-
// @TODO: unique
|
|
66
|
+
// @TODO: unique cache parameters per pages
|
|
56
67
|
ttl: 5 * 60 * 1000,
|
|
57
68
|
maxSize: 100,
|
|
58
69
|
allowStale: true,
|
|
70
|
+
// @TODO: unique headers per pages
|
|
59
71
|
allowedHeaders: [],
|
|
72
|
+
// @TODO: unique query per pages
|
|
73
|
+
allowedQuery: [],
|
|
60
74
|
},
|
|
61
75
|
}),
|
|
62
76
|
core.provide({
|
|
@@ -174,9 +188,10 @@ const staticPagesProviders = [
|
|
|
174
188
|
core.provide({
|
|
175
189
|
provide: privateTokens.STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE,
|
|
176
190
|
useFactory: ({ requestManager, pageService, router, defaultRenderMode, fileSystemCache }) => {
|
|
177
|
-
return () => getPageRenderMode.getPageRenderMode({
|
|
191
|
+
return (route) => getPageRenderMode.getPageRenderMode({
|
|
178
192
|
pageService,
|
|
179
193
|
router,
|
|
194
|
+
route,
|
|
180
195
|
requestManager,
|
|
181
196
|
defaultRenderMode,
|
|
182
197
|
fileSystemCache,
|
|
@@ -244,12 +259,13 @@ const staticPagesProviders = [
|
|
|
244
259
|
provide: staticPagesCommandLine
|
|
245
260
|
? core.commandLineListTokens[staticPagesCommandLine]
|
|
246
261
|
: core.commandLineListTokens.customerStart,
|
|
247
|
-
useFactory: ({ staticPagesService, staticPagesCacheMetrics, logger, requestManager, responseManager, staticPagesKey, linkPrefetchManager, resolvePageRenderMode, router, pageRegistry, }) => {
|
|
262
|
+
useFactory: ({ staticPagesService, staticPagesCacheMetrics, logger, requestManager, responseManager, staticPagesKey, linkPrefetchManager, resolvePageRenderMode, router: router$1, pageRegistry, routeTree, }) => {
|
|
248
263
|
const log = logger('static-pages');
|
|
264
|
+
// eslint-disable-next-line max-statements
|
|
249
265
|
return async function staticPagesFromCache() {
|
|
250
266
|
const isPrerenderRequest = !!requestManager.getHeader('x-tramvai-prerender');
|
|
251
267
|
const { pathname } = requestManager.getParsedUrl();
|
|
252
|
-
|
|
268
|
+
let route = router$1?.getCurrentRoute() ?? router$1?.resolve(pathname);
|
|
253
269
|
// prefetch route only for `tramvai static` because it can be async and slow in runtime
|
|
254
270
|
if (isPrerenderRequest) {
|
|
255
271
|
log.debug(`Should prefetch route and component to determine if page is static: ${pathname}`);
|
|
@@ -264,8 +280,22 @@ const staticPagesProviders = [
|
|
|
264
280
|
}
|
|
265
281
|
});
|
|
266
282
|
}
|
|
267
|
-
|
|
283
|
+
// dynamic route will be resolved after prefetch
|
|
284
|
+
if (isPrerenderRequest && !route) {
|
|
285
|
+
route = router$1?.getCurrentRoute() ?? router$1?.resolve(pathname);
|
|
286
|
+
}
|
|
287
|
+
const isStatic = resolvePageRenderMode(route) === 'static';
|
|
268
288
|
const shouldUseCache = staticPagesService.shouldUseCache();
|
|
289
|
+
// we can't use cache for wildcard routes,
|
|
290
|
+
// because wildcard routes can match any possible urls
|
|
291
|
+
const isWildcardRoute = route
|
|
292
|
+
? router.isWildcard(route.path)
|
|
293
|
+
: routeTree.getWildcard(pathname);
|
|
294
|
+
// routes can be resolved in runtime with ROUTE_RESOLVE_TOKEN,
|
|
295
|
+
// but this routes exists only in Request scope, so we use STATIC_PAGES_ROUTE_TREE
|
|
296
|
+
// to save minimal info about resolved routes between requests, because
|
|
297
|
+
// we want to be sure that we use cache and revalidation only for resolved routes
|
|
298
|
+
const isUnknownRoute = !route && !routeTree.getRoute(pathname);
|
|
269
299
|
if (isStatic) {
|
|
270
300
|
responseManager.setHeader('X-Tramvai-Static-Page-Key', staticPagesKey());
|
|
271
301
|
// we need to tell for `tramvai static` prerendering command that this page has `static` render mode,
|
|
@@ -274,16 +304,23 @@ const staticPagesProviders = [
|
|
|
274
304
|
if (isPrerenderRequest && route) {
|
|
275
305
|
responseManager.setHeader('X-Tramvai-Static-Page-Route', JSON.stringify(route));
|
|
276
306
|
}
|
|
277
|
-
if (shouldUseCache) {
|
|
278
|
-
log.
|
|
307
|
+
if (shouldUseCache && !isWildcardRoute && !isUnknownRoute) {
|
|
308
|
+
log.info(`Should use static pages cache: ${pathname}`);
|
|
279
309
|
await staticPagesService.respond(() => {
|
|
280
|
-
log.
|
|
310
|
+
log.info(`Successful static page response from cache: ${pathname}`);
|
|
281
311
|
staticPagesCacheMetrics.hit.inc();
|
|
282
|
-
throw
|
|
312
|
+
throw STOP_COMMAND_LINE_ERROR;
|
|
283
313
|
});
|
|
284
314
|
}
|
|
285
315
|
else {
|
|
286
|
-
|
|
316
|
+
let msg = `Static pages cache is not used for this request: ${pathname}`;
|
|
317
|
+
if (shouldUseCache && isWildcardRoute) {
|
|
318
|
+
msg += `, because wildcard route was resolved`;
|
|
319
|
+
}
|
|
320
|
+
else if (shouldUseCache && isUnknownRoute) {
|
|
321
|
+
msg += `, because route for this url was never resolved`;
|
|
322
|
+
}
|
|
323
|
+
log.info(msg);
|
|
287
324
|
}
|
|
288
325
|
}
|
|
289
326
|
};
|
|
@@ -299,6 +336,7 @@ const staticPagesProviders = [
|
|
|
299
336
|
resolvePageRenderMode: privateTokens.STATIC_PAGES_RESOLVE_PAGE_RENDER_MODE,
|
|
300
337
|
router: core.optional(tokensRouter.ROUTER_TOKEN),
|
|
301
338
|
pageRegistry: core.optional(tokensRouter.PAGE_REGISTRY_TOKEN),
|
|
339
|
+
routeTree: tokens.STATIC_PAGES_ROUTE_TREE,
|
|
302
340
|
},
|
|
303
341
|
});
|
|
304
342
|
};
|
|
@@ -371,6 +409,37 @@ const staticPagesProviders = [
|
|
|
371
409
|
logger: tokensCommon.LOGGER_TOKEN,
|
|
372
410
|
},
|
|
373
411
|
}),
|
|
412
|
+
core.provide({
|
|
413
|
+
provide: tokens.STATIC_PAGES_ROUTE_TREE,
|
|
414
|
+
scope: core.Scope.SINGLETON,
|
|
415
|
+
useFactory: ({ di }) => {
|
|
416
|
+
const staticRoutes = di.get(core.optional(tokensRouter.ROUTES_TOKEN)) ?? [];
|
|
417
|
+
return new router.RouteTree(flatten__default["default"](staticRoutes).map((route) => ({ name: route.name, path: route.path })));
|
|
418
|
+
},
|
|
419
|
+
deps: {
|
|
420
|
+
di: core.DI_TOKEN,
|
|
421
|
+
},
|
|
422
|
+
}),
|
|
423
|
+
core.provide({
|
|
424
|
+
provide: core.commandLineListTokens.clear,
|
|
425
|
+
useFactory: ({ router, routeTree }) => {
|
|
426
|
+
return () => {
|
|
427
|
+
const currentUrl = router.getCurrentUrl();
|
|
428
|
+
const currentRoute = router.getCurrentRoute();
|
|
429
|
+
// save dynamically resolved route in route tree to use it in next requests before route resolving
|
|
430
|
+
if (currentRoute && currentUrl && !routeTree.getRoute(currentUrl.pathname)) {
|
|
431
|
+
routeTree.addRoute({
|
|
432
|
+
name: currentRoute.name,
|
|
433
|
+
path: currentRoute.path,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
},
|
|
438
|
+
deps: {
|
|
439
|
+
router: tokensRouter.ROUTER_TOKEN,
|
|
440
|
+
routeTree: tokens.STATIC_PAGES_ROUTE_TREE,
|
|
441
|
+
},
|
|
442
|
+
}),
|
|
374
443
|
];
|
|
375
444
|
|
|
376
445
|
exports.staticPagesProviders = staticPagesProviders;
|
package/lib/tokens.browser.js
CHANGED
|
@@ -28,5 +28,8 @@ const STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN = createToken('static pages cache-
|
|
|
28
28
|
scope: Scope.REQUEST,
|
|
29
29
|
});
|
|
30
30
|
const STATIC_PAGES_SERVICE = createToken('static pages service');
|
|
31
|
+
const STATIC_PAGES_ROUTE_TREE = createToken('static pages route tree', {
|
|
32
|
+
scope: Scope.SINGLETON,
|
|
33
|
+
});
|
|
31
34
|
|
|
32
|
-
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE };
|
|
35
|
+
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_ROUTE_TREE, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE };
|
package/lib/tokens.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TramvaiRenderMode } from '@tramvai/tokens-render';
|
|
2
2
|
import type { Cache, ResponseManager } from '@tramvai/tokens-common';
|
|
3
|
+
import { RouteTree } from '@tinkoff/router';
|
|
3
4
|
import { StaticPagesService } from './staticPages/staticPagesService';
|
|
4
5
|
export declare const PAGE_RENDER_FALLBACK_COMPONENT_PREFIX: string & {
|
|
5
6
|
__type?: "base token" | undefined;
|
|
@@ -53,10 +54,18 @@ export interface StaticPagesOptions {
|
|
|
53
54
|
allowStale: boolean;
|
|
54
55
|
/**
|
|
55
56
|
* Whitelist of headers, that are allowed to be passed from client request to backround cache revalidation request,
|
|
56
|
-
* and allowed to be cached and returned with the cached response
|
|
57
|
+
* and allowed to be cached and returned with the cached response.
|
|
58
|
+
* Some headers are required and can't be removed from this list:
|
|
59
|
+
* ['Location', 'Content-Type', 'Content-Length', 'X-App-Id', 'X-Original-Host', 'Host', 'X-Forwarded-Host', 'X-Forwarded-Proto']
|
|
57
60
|
* @default []
|
|
58
61
|
*/
|
|
59
62
|
allowedHeaders: string[];
|
|
63
|
+
/**
|
|
64
|
+
* Whitelist of query parameters, that are allowed to be passed from client request to backround cache revalidation request,
|
|
65
|
+
* and allowed to be cached and returned with the cached response
|
|
66
|
+
* @default []
|
|
67
|
+
*/
|
|
68
|
+
allowedQuery: string[];
|
|
60
69
|
}
|
|
61
70
|
export declare const STATIC_PAGES_CACHE_TOKEN: Cache<StaticPagesCacheEntry> & {
|
|
62
71
|
__type?: "base token" | undefined;
|
|
@@ -142,4 +151,7 @@ export declare const STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN: ((entry: {
|
|
|
142
151
|
export declare const STATIC_PAGES_SERVICE: StaticPagesService & {
|
|
143
152
|
__type?: "base token" | undefined;
|
|
144
153
|
};
|
|
154
|
+
export declare const STATIC_PAGES_ROUTE_TREE: RouteTree & {
|
|
155
|
+
__type?: "base token" | undefined;
|
|
156
|
+
};
|
|
145
157
|
//# sourceMappingURL=tokens.d.ts.map
|
package/lib/tokens.es.js
CHANGED
|
@@ -28,5 +28,8 @@ const STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN = createToken('static pages cache-
|
|
|
28
28
|
scope: Scope.REQUEST,
|
|
29
29
|
});
|
|
30
30
|
const STATIC_PAGES_SERVICE = createToken('static pages service');
|
|
31
|
+
const STATIC_PAGES_ROUTE_TREE = createToken('static pages route tree', {
|
|
32
|
+
scope: Scope.SINGLETON,
|
|
33
|
+
});
|
|
31
34
|
|
|
32
|
-
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE };
|
|
35
|
+
export { PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT, PAGE_RENDER_DEFAULT_MODE, PAGE_RENDER_FALLBACK_COMPONENT_PREFIX, PAGE_RENDER_WRAPPER_TYPE, STATIC_PAGES_BACKGROUND_FETCH_ENABLED, STATIC_PAGES_CACHE_5xx_RESPONSE, STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN, STATIC_PAGES_CACHE_TOKEN, STATIC_PAGES_COMMAND_LINE, STATIC_PAGES_FS_CACHE_ENABLED, STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN, STATIC_PAGES_KEY_TOKEN, STATIC_PAGES_MODIFY_CACHE, STATIC_PAGES_OPTIONS_TOKEN, STATIC_PAGES_ROUTE_TREE, STATIC_PAGES_SERVICE, STATIC_PAGES_SHOULD_USE_CACHE };
|
package/lib/tokens.js
CHANGED
|
@@ -32,6 +32,9 @@ const STATIC_PAGES_CACHE_CONTROL_HEADER_TOKEN = dippy.createToken('static pages
|
|
|
32
32
|
scope: dippy.Scope.REQUEST,
|
|
33
33
|
});
|
|
34
34
|
const STATIC_PAGES_SERVICE = dippy.createToken('static pages service');
|
|
35
|
+
const STATIC_PAGES_ROUTE_TREE = dippy.createToken('static pages route tree', {
|
|
36
|
+
scope: dippy.Scope.SINGLETON,
|
|
37
|
+
});
|
|
35
38
|
|
|
36
39
|
exports.PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT = PAGE_RENDER_DEFAULT_FALLBACK_COMPONENT;
|
|
37
40
|
exports.PAGE_RENDER_DEFAULT_MODE = PAGE_RENDER_DEFAULT_MODE;
|
|
@@ -47,5 +50,6 @@ exports.STATIC_PAGES_FS_CACHE_OPTIONS_TOKEN = STATIC_PAGES_FS_CACHE_OPTIONS_TOKE
|
|
|
47
50
|
exports.STATIC_PAGES_KEY_TOKEN = STATIC_PAGES_KEY_TOKEN;
|
|
48
51
|
exports.STATIC_PAGES_MODIFY_CACHE = STATIC_PAGES_MODIFY_CACHE;
|
|
49
52
|
exports.STATIC_PAGES_OPTIONS_TOKEN = STATIC_PAGES_OPTIONS_TOKEN;
|
|
53
|
+
exports.STATIC_PAGES_ROUTE_TREE = STATIC_PAGES_ROUTE_TREE;
|
|
50
54
|
exports.STATIC_PAGES_SERVICE = STATIC_PAGES_SERVICE;
|
|
51
55
|
exports.STATIC_PAGES_SHOULD_USE_CACHE = STATIC_PAGES_SHOULD_USE_CACHE;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const getPageRenderMode = ({ requestManager, router, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
2
|
-
const resolvedRoute = router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
1
|
+
const getPageRenderMode = ({ requestManager, router, route, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
2
|
+
const resolvedRoute = route ?? router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
3
3
|
// if we can't resolve route, it means that we are on 404 page, or has dynamic routing,
|
|
4
4
|
// so we can't determine render mode by page config or page component property, and should fallback to default render mode
|
|
5
5
|
if (!resolvedRoute) {
|
|
@@ -2,11 +2,13 @@ import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
|
2
2
|
import type { TramvaiRenderMode } from '@tramvai/tokens-render';
|
|
3
3
|
import type { PAGE_SERVICE_TOKEN, ROUTER_TOKEN } from '@tramvai/tokens-router';
|
|
4
4
|
import { REQUEST_MANAGER_TOKEN } from '@tramvai/tokens-common';
|
|
5
|
+
import { Route } from '@tinkoff/router';
|
|
5
6
|
import type { PAGE_RENDER_DEFAULT_MODE } from '../tokens';
|
|
6
7
|
import { STATIC_PAGES_FS_CACHE_TOKEN } from '../private-tokens';
|
|
7
|
-
export declare const getPageRenderMode: ({ requestManager, router, pageService, defaultRenderMode, fileSystemCache, }: {
|
|
8
|
+
export declare const getPageRenderMode: ({ requestManager, router, route, pageService, defaultRenderMode, fileSystemCache, }: {
|
|
8
9
|
requestManager: ExtractDependencyType<typeof REQUEST_MANAGER_TOKEN>;
|
|
9
10
|
router: ExtractDependencyType<typeof ROUTER_TOKEN> | null;
|
|
11
|
+
route?: Route | undefined;
|
|
10
12
|
pageService: ExtractDependencyType<typeof PAGE_SERVICE_TOKEN> | null;
|
|
11
13
|
defaultRenderMode: ExtractDependencyType<typeof PAGE_RENDER_DEFAULT_MODE>;
|
|
12
14
|
fileSystemCache?: import("../staticPages/fileSystemCache").FileSystemCache | null | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const getPageRenderMode = ({ requestManager, router, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
2
|
-
const resolvedRoute = router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
1
|
+
const getPageRenderMode = ({ requestManager, router, route, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
2
|
+
const resolvedRoute = route ?? router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
3
3
|
// if we can't resolve route, it means that we are on 404 page, or has dynamic routing,
|
|
4
4
|
// so we can't determine render mode by page config or page component property, and should fallback to default render mode
|
|
5
5
|
if (!resolvedRoute) {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
const getPageRenderMode = ({ requestManager, router, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
6
|
-
const resolvedRoute = router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
5
|
+
const getPageRenderMode = ({ requestManager, router, route, pageService, defaultRenderMode, fileSystemCache, }) => {
|
|
6
|
+
const resolvedRoute = route ?? router?.getCurrentRoute() ?? router?.resolve(requestManager.getParsedUrl().pathname);
|
|
7
7
|
// if we can't resolve route, it means that we are on 404 page, or has dynamic routing,
|
|
8
8
|
// so we can't determine render mode by page config or page component property, and should fallback to default render mode
|
|
9
9
|
if (!resolvedRoute) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-page-render-mode",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.16.0",
|
|
4
4
|
"description": "Enable different rendering modes for pages",
|
|
5
5
|
"main": "./lib/server.js",
|
|
6
6
|
"module": "./lib/server.es.js",
|
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
"p-limit": "^3"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@tinkoff/dippy": "0.
|
|
32
|
-
"@tinkoff/router": "0.7.
|
|
31
|
+
"@tinkoff/dippy": "^1.0.0",
|
|
32
|
+
"@tinkoff/router": "0.7.55",
|
|
33
33
|
"@tinkoff/utils": "^2.1.2",
|
|
34
|
-
"@tramvai/core": "7.
|
|
35
|
-
"@tramvai/module-client-hints": "7.
|
|
36
|
-
"@tramvai/module-router": "7.
|
|
37
|
-
"@tramvai/papi": "7.
|
|
38
|
-
"@tramvai/react": "7.
|
|
39
|
-
"@tramvai/tokens-common": "7.
|
|
40
|
-
"@tramvai/tokens-core": "7.
|
|
41
|
-
"@tramvai/tokens-metrics": "7.
|
|
42
|
-
"@tramvai/tokens-render": "7.
|
|
43
|
-
"@tramvai/tokens-router": "7.
|
|
44
|
-
"@tramvai/tokens-server": "7.
|
|
45
|
-
"@tramvai/tokens-server-private": "7.
|
|
34
|
+
"@tramvai/core": "7.16.0",
|
|
35
|
+
"@tramvai/module-client-hints": "7.16.0",
|
|
36
|
+
"@tramvai/module-router": "7.16.0",
|
|
37
|
+
"@tramvai/papi": "7.16.0",
|
|
38
|
+
"@tramvai/react": "7.16.0",
|
|
39
|
+
"@tramvai/tokens-common": "7.16.0",
|
|
40
|
+
"@tramvai/tokens-core": "7.16.0",
|
|
41
|
+
"@tramvai/tokens-metrics": "7.16.0",
|
|
42
|
+
"@tramvai/tokens-render": "7.16.0",
|
|
43
|
+
"@tramvai/tokens-router": "7.16.0",
|
|
44
|
+
"@tramvai/tokens-server": "7.16.0",
|
|
45
|
+
"@tramvai/tokens-server-private": "7.16.0",
|
|
46
46
|
"prom-client": "^14.2.0",
|
|
47
47
|
"react": ">=16.14.0",
|
|
48
48
|
"tslib": "^2.4.0"
|