proteum 2.1.6 → 2.1.8
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/AGENTS.md +6 -1
- package/README.md +5 -1
- package/agents/project/AGENTS.md +7 -1
- package/agents/project/diagnostics.md +4 -0
- package/agents/project/optimizations.md +1 -0
- package/cli/bin.js +0 -8
- package/cli/commands/build.ts +60 -9
- package/cli/commands/dev.ts +236 -6
- package/cli/compiler/artifacts/manifest.ts +8 -3
- package/cli/compiler/common/bundleAnalysis.ts +56 -1
- package/cli/index.ts +37 -2
- package/cli/presentation/commands.ts +30 -4
- package/cli/presentation/devSession.ts +6 -26
- package/cli/presentation/help.ts +4 -0
- package/cli/presentation/welcome.ts +63 -0
- package/cli/runtime/commands.ts +40 -3
- package/cli/runtime/devSessions.ts +337 -0
- package/cli/scaffold/index.ts +4 -2
- package/cli/scaffold/templates.ts +7 -1
- package/cli/utils/agents.ts +102 -11
- package/package.json +1 -1
- package/server/app/container/index.ts +7 -1
- package/server/services/router/http/index.ts +52 -24
- package/server/services/router/index.ts +66 -10
- package/server/services/router/response/page/document.tsx +26 -14
|
@@ -95,6 +95,9 @@ export type TApiResponseData = { data: any; triggers?: { [cle: string]: any } };
|
|
|
95
95
|
|
|
96
96
|
export type HttpHeaders = { [cle: string]: string };
|
|
97
97
|
|
|
98
|
+
const dynamicHtmlCacheControl = 'no-store, no-cache, must-revalidate, proxy-revalidate';
|
|
99
|
+
const staticHtmlCacheControl = 'public, max-age=0, must-revalidate';
|
|
100
|
+
|
|
98
101
|
/*----------------------------------
|
|
99
102
|
- SERVICE CONFIG
|
|
100
103
|
----------------------------------*/
|
|
@@ -571,14 +574,10 @@ export default class ServerRouter<
|
|
|
571
574
|
- RESOLUTION
|
|
572
575
|
----------------------------------*/
|
|
573
576
|
public async middleware(req: express.Request, res: express.Response) {
|
|
574
|
-
// Don't cache HTML, because in case of update, assets file name will change (hash.ext)
|
|
575
|
-
// https://github.com/helmetjs/nocache/blob/main/index.ts
|
|
576
|
-
res.setHeader('Surrogate-Control', 'no-store');
|
|
577
|
-
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
578
|
-
|
|
579
577
|
// Create request
|
|
580
578
|
let requestId = uuid();
|
|
581
579
|
const cachedPage = req.headers['bypasscache'] ? undefined : this.cache[req.path];
|
|
580
|
+
this.applyHtmlCacheHeaders(res, Boolean(cachedPage));
|
|
582
581
|
const headers: HttpHeaders = Object.fromEntries(
|
|
583
582
|
Object.entries(req.headers).map(([key, value]) => [key, Array.isArray(value) ? value.join(', ') : value || '']),
|
|
584
583
|
);
|
|
@@ -638,10 +637,39 @@ export default class ServerRouter<
|
|
|
638
637
|
// Static pages
|
|
639
638
|
if (cachedPage) {
|
|
640
639
|
console.log('[router] Get static page from cache', req.path);
|
|
640
|
+
res.status(response.statusCode);
|
|
641
|
+
res.header(response.headers);
|
|
642
|
+
|
|
643
|
+
if (response.headers['Location']) {
|
|
644
|
+
res.send(response.data === undefined ? '' : response.data);
|
|
645
|
+
this.app.container.Trace.record(
|
|
646
|
+
request.id,
|
|
647
|
+
'response.send',
|
|
648
|
+
{
|
|
649
|
+
cached: true,
|
|
650
|
+
statusCode: response.statusCode,
|
|
651
|
+
contentType: response.headers['Content-Type'] || '',
|
|
652
|
+
headerKeys: Object.keys(response.headers),
|
|
653
|
+
redirected: true,
|
|
654
|
+
},
|
|
655
|
+
'summary',
|
|
656
|
+
);
|
|
657
|
+
this.app.container.Trace.finishRequest(request.id, {
|
|
658
|
+
statusCode: response.statusCode,
|
|
659
|
+
user: request.user?.email,
|
|
660
|
+
});
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
641
664
|
this.app.container.Trace.record(
|
|
642
665
|
request.id,
|
|
643
666
|
'response.send',
|
|
644
|
-
{
|
|
667
|
+
{
|
|
668
|
+
cached: true,
|
|
669
|
+
statusCode: response.statusCode,
|
|
670
|
+
contentType: response.headers['Content-Type'] || 'text/html',
|
|
671
|
+
headerKeys: Object.keys(response.headers),
|
|
672
|
+
},
|
|
645
673
|
'summary',
|
|
646
674
|
);
|
|
647
675
|
res.send(cachedPage.rendered);
|
|
@@ -870,6 +898,12 @@ export default class ServerRouter<
|
|
|
870
898
|
}
|
|
871
899
|
|
|
872
900
|
this.app.container.Trace.record(request.id, 'resolve.routes-evaluated', routeStats, 'resolve');
|
|
901
|
+
|
|
902
|
+
if (isStatic) {
|
|
903
|
+
resolve(response);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
873
907
|
this.app.container.Trace.record(request.id, 'resolve.not-found', { path: request.path }, 'summary');
|
|
874
908
|
reject(new NotFound());
|
|
875
909
|
} catch (error) {
|
|
@@ -923,10 +957,19 @@ export default class ServerRouter<
|
|
|
923
957
|
await response.runController(route);
|
|
924
958
|
if (!response.wasProvided) return;
|
|
925
959
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
960
|
+
if (response.request.path && route.options.static) {
|
|
961
|
+
const staticUrls = route.options.static.urls.includes('*') ? [response.request.path] : route.options.static.urls;
|
|
962
|
+
|
|
963
|
+
for (const staticUrl of staticUrls) {
|
|
964
|
+
if (!staticUrl) continue;
|
|
965
|
+
|
|
966
|
+
console.log('[router] Set in cache', staticUrl);
|
|
967
|
+
void this.renderStatic(
|
|
968
|
+
staticUrl,
|
|
969
|
+
route.options.static,
|
|
970
|
+
staticUrl === response.request.path ? response.data : undefined,
|
|
971
|
+
);
|
|
972
|
+
}
|
|
930
973
|
}
|
|
931
974
|
|
|
932
975
|
const timeEndResolving = Date.now();
|
|
@@ -1008,4 +1051,17 @@ export default class ServerRouter<
|
|
|
1008
1051
|
|
|
1009
1052
|
return response;
|
|
1010
1053
|
}
|
|
1054
|
+
|
|
1055
|
+
private applyHtmlCacheHeaders(res: express.Response, isStaticHtml: boolean) {
|
|
1056
|
+
if (isStaticHtml) {
|
|
1057
|
+
res.removeHeader('Surrogate-Control');
|
|
1058
|
+
res.setHeader('Cache-Control', staticHtmlCacheControl);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Don't cache dynamic HTML, because updated releases can change asset hashes.
|
|
1063
|
+
// https://github.com/helmetjs/nocache/blob/main/index.ts
|
|
1064
|
+
res.setHeader('Surrogate-Control', 'no-store');
|
|
1065
|
+
res.setHeader('Cache-Control', dynamicHtmlCacheControl);
|
|
1066
|
+
}
|
|
1011
1067
|
}
|
|
@@ -34,12 +34,10 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
34
34
|
<head>
|
|
35
35
|
{/* Format */}
|
|
36
36
|
<meta charSet="utf-8" />
|
|
37
|
-
<meta name="viewport" content="width=device-width,
|
|
37
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
38
38
|
|
|
39
39
|
{/* CSS */}
|
|
40
40
|
{this.clientStyles()}
|
|
41
|
-
<link rel="preload" as="font" href={'/public/icons.woff2?v=' + BUILD_ID} type="font/woff2" />
|
|
42
|
-
<link rel="stylesheet" type="text/css" href="/public/icons.css" />
|
|
43
41
|
|
|
44
42
|
{/* JS */}
|
|
45
43
|
<script
|
|
@@ -69,7 +67,7 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
69
67
|
{/* Format */}
|
|
70
68
|
<meta charSet="utf-8" />
|
|
71
69
|
<meta content="IE=edge" httpEquiv="X-UA-Compatible" />
|
|
72
|
-
<meta name="viewport" content="width=device-width,
|
|
70
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
73
71
|
|
|
74
72
|
{/* Mobile */}
|
|
75
73
|
<meta name="application-name" content={this.app.identity.web.title} />
|
|
@@ -80,18 +78,28 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
80
78
|
<meta name="msapplication-TileColor" content={this.app.identity.maincolor} />
|
|
81
79
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
82
80
|
<meta name="mobile-web-app-capable" content="yes" />
|
|
83
|
-
<meta
|
|
84
|
-
name="viewport"
|
|
85
|
-
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
|
|
86
|
-
/>
|
|
87
81
|
|
|
88
82
|
{/* https://stackoverflow.com/questions/48956465/favicon-standard-2019-svg-ico-png-and-dimensions */}
|
|
89
83
|
{/*<link rel="manifest" href={RES['manifest.json']} />*/}
|
|
90
|
-
<link rel="shortcut icon" href=
|
|
91
|
-
<link
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
<link rel="shortcut icon" href={this.publicAssetUrl('app/favicon.ico', true)} />
|
|
85
|
+
<link
|
|
86
|
+
rel="icon"
|
|
87
|
+
type="image/png"
|
|
88
|
+
sizes="16x16"
|
|
89
|
+
href={this.publicAssetUrl('app/favicon-16x16.png', true)}
|
|
90
|
+
/>
|
|
91
|
+
<link
|
|
92
|
+
rel="icon"
|
|
93
|
+
type="image/png"
|
|
94
|
+
sizes="32x32"
|
|
95
|
+
href={this.publicAssetUrl('app/favicon-32x32.png', true)}
|
|
96
|
+
/>
|
|
97
|
+
<link
|
|
98
|
+
rel="apple-touch-icon"
|
|
99
|
+
sizes="180x180"
|
|
100
|
+
href={this.publicAssetUrl('app/apple-touch-icon-180x180.png', true)}
|
|
101
|
+
/>
|
|
102
|
+
<meta name="msapplication-config" content={this.publicAssetUrl('app/browserconfig.xml', true)} />
|
|
95
103
|
|
|
96
104
|
{/* Page */}
|
|
97
105
|
<title>{page.title}</title>
|
|
@@ -165,7 +173,7 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
165
173
|
return (
|
|
166
174
|
<>
|
|
167
175
|
{scripts.map((script) => {
|
|
168
|
-
const src = this.clientAssetUrl(script
|
|
176
|
+
const src = this.clientAssetUrl(script);
|
|
169
177
|
|
|
170
178
|
return (
|
|
171
179
|
<React.Fragment key={script}>
|
|
@@ -248,6 +256,10 @@ export default class DocumentRenderer<TRouter extends TServerRouter> {
|
|
|
248
256
|
}
|
|
249
257
|
|
|
250
258
|
private clientAssetUrl(asset: string, withBuildId = false) {
|
|
259
|
+
return this.publicAssetUrl(asset, withBuildId);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private publicAssetUrl(asset: string, withBuildId = false) {
|
|
251
263
|
return `/public/${asset}${withBuildId ? `?v=${BUILD_ID}` : ''}`;
|
|
252
264
|
}
|
|
253
265
|
}
|