@blinkk/root 3.0.6 → 3.1.1
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/dist/build-worker.js +108 -0
- package/dist/build-worker.js.map +7 -0
- package/dist/chunk-3PWR4F2R.js +77 -0
- package/dist/chunk-3PWR4F2R.js.map +7 -0
- package/dist/{chunk-3EFECH2D.js → chunk-E4F6CMO3.js} +439 -553
- package/dist/chunk-E4F6CMO3.js.map +7 -0
- package/dist/{chunk-7HK6F5RQ.js → chunk-I3RCAIW7.js} +1 -75
- package/dist/chunk-I3RCAIW7.js.map +7 -0
- package/dist/chunk-KHO3HMX6.js +291 -0
- package/dist/chunk-KHO3HMX6.js.map +7 -0
- package/dist/chunk-M3E4RKS7.js +374 -0
- package/dist/chunk-M3E4RKS7.js.map +7 -0
- package/dist/{chunk-6NBKAR5Y.js → chunk-TM6QRBGS.js} +31 -1
- package/dist/chunk-TM6QRBGS.js.map +7 -0
- package/dist/chunk-WX62JK5E.js +81 -0
- package/dist/chunk-WX62JK5E.js.map +7 -0
- package/dist/{chunk-7PSEEE6C.js → chunk-YU22SAIG.js} +31 -317
- package/dist/chunk-YU22SAIG.js.map +7 -0
- package/dist/cli/build-page.d.ts +57 -0
- package/dist/cli/build-worker-pool.d.ts +46 -0
- package/dist/cli/build-worker.d.ts +4 -0
- package/dist/cli/build.d.ts +6 -0
- package/dist/cli.js +8 -4
- package/dist/core/security.d.ts +14 -0
- package/dist/functions.js +8 -4
- package/dist/functions.js.map +1 -1
- package/dist/middleware/common.d.ts +18 -0
- package/dist/middleware/compression.d.ts +12 -0
- package/dist/middleware/middleware.d.ts +1 -0
- package/dist/middleware.js +6 -1
- package/dist/node.js +7 -5
- package/dist/render/render.d.ts +15 -9
- package/dist/render.js +42 -95
- package/dist/render.js.map +2 -2
- package/package.json +2 -2
- package/dist/chunk-3EFECH2D.js.map +0 -7
- package/dist/chunk-6NBKAR5Y.js.map +0 -7
- package/dist/chunk-7HK6F5RQ.js.map +0 -7
- package/dist/chunk-7PSEEE6C.js.map +0 -7
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,15 @@ import {
|
|
|
8
8
|
dev,
|
|
9
9
|
preview,
|
|
10
10
|
start
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
14
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-E4F6CMO3.js";
|
|
12
|
+
import "./chunk-YU22SAIG.js";
|
|
13
|
+
import "./chunk-I3RCAIW7.js";
|
|
14
|
+
import "./chunk-M3E4RKS7.js";
|
|
15
|
+
import "./chunk-KHO3HMX6.js";
|
|
16
|
+
import "./chunk-3PWR4F2R.js";
|
|
15
17
|
import "./chunk-XSNCF7WU.js";
|
|
18
|
+
import "./chunk-TM6QRBGS.js";
|
|
19
|
+
import "./chunk-WX62JK5E.js";
|
|
16
20
|
export {
|
|
17
21
|
CliRunner,
|
|
18
22
|
build,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RootConfig, RootSecurityConfig } from './config.js';
|
|
2
|
+
import { Response } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the `security` config value with default values inserted wherever
|
|
5
|
+
* a user config value is blank or set to `true`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getSecurityConfig(rootConfig: RootConfig): Required<RootSecurityConfig>;
|
|
8
|
+
/**
|
|
9
|
+
* Sets security-related HTTP headers.
|
|
10
|
+
*/
|
|
11
|
+
export declare function setSecurityHeaders(res: Response, options: {
|
|
12
|
+
securityConfig: Required<RootSecurityConfig>;
|
|
13
|
+
nonce?: string;
|
|
14
|
+
}): void;
|
package/dist/functions.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createPreviewServer,
|
|
3
3
|
createProdServer
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-E4F6CMO3.js";
|
|
5
|
+
import "./chunk-YU22SAIG.js";
|
|
6
|
+
import "./chunk-I3RCAIW7.js";
|
|
7
|
+
import "./chunk-M3E4RKS7.js";
|
|
8
|
+
import "./chunk-KHO3HMX6.js";
|
|
9
|
+
import "./chunk-3PWR4F2R.js";
|
|
8
10
|
import "./chunk-XSNCF7WU.js";
|
|
11
|
+
import "./chunk-TM6QRBGS.js";
|
|
12
|
+
import "./chunk-WX62JK5E.js";
|
|
9
13
|
|
|
10
14
|
// src/functions/server.ts
|
|
11
15
|
import path from "node:path";
|
package/dist/functions.js.map
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/functions/server.ts"],
|
|
4
4
|
"sourcesContent": ["import path from 'node:path';\nimport {HttpsOptions, onRequest} from 'firebase-functions/v2/https';\nimport {createPreviewServer, createProdServer} from '../cli/cli.js';\nimport {Server} from '../core/types.js';\n\nexport interface ProdServerOptions {\n rootDir?: string;\n mode?: 'preview' | 'production';\n httpsOptions?: HttpsOptions;\n}\n\n/**\n * Firebase Function that runs a Root.js server running in SSR mode.\n */\nexport function server(options?: ProdServerOptions) {\n let rootServer: Server;\n const rootDir = path.resolve(options?.rootDir || process.cwd());\n return onRequest(options?.httpsOptions || {}, async (req, res) => {\n if (!rootServer) {\n if (options?.mode === 'preview') {\n rootServer = await createPreviewServer({rootDir});\n } else {\n rootServer = await createProdServer({rootDir});\n }\n }\n await rootServer(req, res);\n });\n}\n"],
|
|
5
|
-
"mappings": "
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;AAAA,OAAO,UAAU;AACjB,SAAsB,iBAAgB;AAa/B,SAAS,OAAO,SAA6B;AAClD,MAAI;AACJ,QAAM,UAAU,KAAK,QAAQ,SAAS,WAAW,QAAQ,IAAI,CAAC;AAC9D,SAAO,UAAU,SAAS,gBAAgB,CAAC,GAAG,OAAO,KAAK,QAAQ;AAChE,QAAI,CAAC,YAAY;AACf,UAAI,SAAS,SAAS,WAAW;AAC/B,qBAAa,MAAM,oBAAoB,EAAC,QAAO,CAAC;AAAA,MAClD,OAAO;AACL,qBAAa,MAAM,iBAAiB,EAAC,QAAO,CAAC;AAAA,MAC/C;AAAA,IACF;AACA,UAAM,WAAW,KAAK,GAAG;AAAA,EAC3B,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -6,6 +6,24 @@ import { Request, Response, NextFunction } from '../core/types.js';
|
|
|
6
6
|
export declare function rootProjectMiddleware(options: {
|
|
7
7
|
rootConfig: RootConfig;
|
|
8
8
|
}): (req: Request, _: Response, next: NextFunction) => void;
|
|
9
|
+
/**
|
|
10
|
+
* Middleware that sets security-related HTTP headers (e.g.
|
|
11
|
+
* `Strict-Transport-Security`) on all responses using the `server.security`
|
|
12
|
+
* config in root.config.ts.
|
|
13
|
+
*
|
|
14
|
+
* The renderer sets these headers on rendered HTML responses, but responses
|
|
15
|
+
* that bypass the renderer (e.g. redirects, static files, plugin-served
|
|
16
|
+
* responses, 404s) would otherwise be served without them. Per
|
|
17
|
+
* https://hstspreload.org, HSTS preload eligibility requires the HSTS header
|
|
18
|
+
* on redirects as well.
|
|
19
|
+
*
|
|
20
|
+
* NOTE: `Content-Security-Policy` is excluded here since the CSP is
|
|
21
|
+
* document-specific and depends on a per-request nonce, which is generated by
|
|
22
|
+
* the renderer at render time.
|
|
23
|
+
*/
|
|
24
|
+
export declare function securityHeadersMiddleware(options: {
|
|
25
|
+
rootConfig: RootConfig;
|
|
26
|
+
}): (req: Request, res: Response, next: NextFunction) => void;
|
|
9
27
|
/**
|
|
10
28
|
* Middleware that injects HTTP headers from the `server.headers` config in
|
|
11
29
|
* root.config.ts.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Response compression middleware shared by the preview and prod servers.
|
|
4
|
+
*
|
|
5
|
+
* Uses brotli when the client accepts it (`Accept-Encoding: br`), falling
|
|
6
|
+
* back to gzip. Brotli quality 4 is used as a balance between compression
|
|
7
|
+
* ratio and CPU cost for dynamically-rendered responses — it generally
|
|
8
|
+
* compresses better than gzip's default (level 6) at comparable speed.
|
|
9
|
+
* Pre-compressed static assets should still be served with higher quality
|
|
10
|
+
* settings by a CDN or static file server where possible.
|
|
11
|
+
*/
|
|
12
|
+
export declare function compressionMiddleware(): (req: Request, res: Response, next: NextFunction) => void;
|
package/dist/middleware.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SESSION_COOKIE,
|
|
3
3
|
Session,
|
|
4
|
+
compressionMiddleware,
|
|
4
5
|
headersMiddleware,
|
|
5
6
|
multipartMiddleware,
|
|
6
7
|
rootProjectMiddleware,
|
|
8
|
+
securityHeadersMiddleware,
|
|
7
9
|
sessionMiddleware,
|
|
8
10
|
trailingSlashMiddleware
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-TM6QRBGS.js";
|
|
12
|
+
import "./chunk-WX62JK5E.js";
|
|
10
13
|
export {
|
|
11
14
|
SESSION_COOKIE,
|
|
12
15
|
Session,
|
|
16
|
+
compressionMiddleware,
|
|
13
17
|
headersMiddleware,
|
|
14
18
|
multipartMiddleware,
|
|
15
19
|
rootProjectMiddleware,
|
|
20
|
+
securityHeadersMiddleware,
|
|
16
21
|
sessionMiddleware,
|
|
17
22
|
trailingSlashMiddleware
|
|
18
23
|
};
|
package/dist/node.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
bundleRootConfig,
|
|
3
2
|
collectPods,
|
|
4
3
|
createViteServer,
|
|
4
|
+
invalidatePodCache,
|
|
5
|
+
viteSsrLoadModule
|
|
6
|
+
} from "./chunk-YU22SAIG.js";
|
|
7
|
+
import {
|
|
8
|
+
bundleRootConfig,
|
|
5
9
|
flattenPackageDepsFromMonorepo,
|
|
6
10
|
getMonorepoPackageDeps,
|
|
7
|
-
invalidatePodCache,
|
|
8
11
|
loadBundledConfig,
|
|
9
12
|
loadPackageJson,
|
|
10
13
|
loadRootConfig,
|
|
11
|
-
loadRootConfigWithDeps
|
|
12
|
-
|
|
13
|
-
} from "./chunk-7PSEEE6C.js";
|
|
14
|
+
loadRootConfigWithDeps
|
|
15
|
+
} from "./chunk-KHO3HMX6.js";
|
|
14
16
|
import "./chunk-XSNCF7WU.js";
|
|
15
17
|
export {
|
|
16
18
|
bundleRootConfig,
|
package/dist/render/render.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export declare class Renderer {
|
|
|
13
13
|
});
|
|
14
14
|
/** Returns a route from the router. */
|
|
15
15
|
getRoute(url: string): [Route | undefined, Record<string, string>];
|
|
16
|
+
/** Returns all routes that match a given url path. */
|
|
17
|
+
getRouteMatches(url: string): Array<[Route, Record<string, string>]>;
|
|
18
|
+
/** Walks all routes registered with the router. */
|
|
19
|
+
walkRoutes(cb: (urlPathFormat: string, route: Route) => void | Promise<void>): Promise<void>;
|
|
16
20
|
handle(req: Request, res: Response, next: NextFunction): Promise<void>;
|
|
17
21
|
private renderComponent;
|
|
18
22
|
/** SSG renders a route. */
|
|
@@ -45,6 +49,17 @@ export declare class Renderer {
|
|
|
45
49
|
*/
|
|
46
50
|
getSitemap(): Promise<Sitemap>;
|
|
47
51
|
private getJsxRenderOptions;
|
|
52
|
+
/**
|
|
53
|
+
* Resolves a synchronous JSX render function, using either the
|
|
54
|
+
* `@blinkk/root/jsx` package or `preact-render-to-string` depending if the
|
|
55
|
+
* `jsxRenderer` config is set up in `root.config.ts`.
|
|
56
|
+
*
|
|
57
|
+
* Resolving the renderer up front (rather than awaiting inside the render
|
|
58
|
+
* call) allows callers that temporarily swap global state (e.g. the
|
|
59
|
+
* `preact.options.vnode` nonce hook) to render synchronously without
|
|
60
|
+
* yielding to the event loop.
|
|
61
|
+
*/
|
|
62
|
+
private getJsxRenderFn;
|
|
48
63
|
/**
|
|
49
64
|
* Renders JSX via either the `@blinkk/root/jsx` package or
|
|
50
65
|
* `preact-render-to-string` depending if the `jsxRenderer` config is set up
|
|
@@ -75,17 +90,8 @@ export declare class Renderer {
|
|
|
75
90
|
* automatically adds the JS/CSS deps to the page.
|
|
76
91
|
*/
|
|
77
92
|
private collectElementDeps;
|
|
78
|
-
/**
|
|
79
|
-
* Returns the `security` config value with default values inserted wherever
|
|
80
|
-
* a user config value is blank or set to `true`.
|
|
81
|
-
*/
|
|
82
|
-
private getSecurityConfig;
|
|
83
93
|
/**
|
|
84
94
|
* Generates a random string that can be used as the "nonce" value for CSP.
|
|
85
95
|
*/
|
|
86
96
|
private generateNonce;
|
|
87
|
-
/**
|
|
88
|
-
* Sets security-related HTTP headers.
|
|
89
|
-
*/
|
|
90
|
-
private setSecurityHeaders;
|
|
91
97
|
}
|
package/dist/render.js
CHANGED
|
@@ -7,14 +7,20 @@ import {
|
|
|
7
7
|
testPathHasParams
|
|
8
8
|
} from "./chunk-KW7KTYGC.js";
|
|
9
9
|
import {
|
|
10
|
-
RouteTrie
|
|
10
|
+
RouteTrie
|
|
11
|
+
} from "./chunk-I3RCAIW7.js";
|
|
12
|
+
import {
|
|
11
13
|
parseTagNames,
|
|
12
14
|
transformHtml
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-3PWR4F2R.js";
|
|
14
16
|
import {
|
|
15
17
|
renderJsxToString
|
|
16
18
|
} from "./chunk-X2C2MEJ2.js";
|
|
17
19
|
import "./chunk-3Z5TNZEP.js";
|
|
20
|
+
import {
|
|
21
|
+
getSecurityConfig,
|
|
22
|
+
setSecurityHeaders
|
|
23
|
+
} from "./chunk-WX62JK5E.js";
|
|
18
24
|
|
|
19
25
|
// src/render/render.tsx
|
|
20
26
|
import crypto from "node:crypto";
|
|
@@ -13119,6 +13125,14 @@ var Renderer = class {
|
|
|
13119
13125
|
getRoute(url) {
|
|
13120
13126
|
return this.router.get(url);
|
|
13121
13127
|
}
|
|
13128
|
+
/** Returns all routes that match a given url path. */
|
|
13129
|
+
getRouteMatches(url) {
|
|
13130
|
+
return this.router.matchAll(url);
|
|
13131
|
+
}
|
|
13132
|
+
/** Walks all routes registered with the router. */
|
|
13133
|
+
async walkRoutes(cb) {
|
|
13134
|
+
await this.router.walk(cb);
|
|
13135
|
+
}
|
|
13122
13136
|
async handle(req, res, next) {
|
|
13123
13137
|
let url = req.path;
|
|
13124
13138
|
if (url.includes("%")) {
|
|
@@ -13170,6 +13184,7 @@ var Renderer = class {
|
|
|
13170
13184
|
scriptDeps: []
|
|
13171
13185
|
};
|
|
13172
13186
|
const vdom = /* @__PURE__ */ jsx4(REQUEST_CONTEXT.Provider, { value: ctx, children: /* @__PURE__ */ jsx4(I18N_CONTEXT.Provider, { value: { locale, translations }, children: /* @__PURE__ */ jsx4(HTML_CONTEXT.Provider, { value: htmlContext, children: /* @__PURE__ */ jsx4(Component, { ...props }) }) }) });
|
|
13187
|
+
const renderJsxFn = await this.getJsxRenderFn();
|
|
13173
13188
|
const preactHook = preactOptions.vnode;
|
|
13174
13189
|
let mainHtml;
|
|
13175
13190
|
try {
|
|
@@ -13187,11 +13202,9 @@ var Renderer = class {
|
|
|
13187
13202
|
preactHook(vnode);
|
|
13188
13203
|
}
|
|
13189
13204
|
};
|
|
13190
|
-
mainHtml =
|
|
13191
|
-
|
|
13192
|
-
} catch (err) {
|
|
13205
|
+
mainHtml = renderJsxFn(vdom);
|
|
13206
|
+
} finally {
|
|
13193
13207
|
preactOptions.vnode = preactHook;
|
|
13194
|
-
throw err;
|
|
13195
13208
|
}
|
|
13196
13209
|
const jsDeps = /* @__PURE__ */ new Set();
|
|
13197
13210
|
const cssDeps = /* @__PURE__ */ new Set();
|
|
@@ -13321,7 +13334,7 @@ var Renderer = class {
|
|
|
13321
13334
|
render404();
|
|
13322
13335
|
return;
|
|
13323
13336
|
}
|
|
13324
|
-
const securityConfig = this.
|
|
13337
|
+
const securityConfig = getSecurityConfig(this.rootConfig);
|
|
13325
13338
|
const cspEnabled = !!securityConfig.contentSecurityPolicy;
|
|
13326
13339
|
const currentPath = req.path;
|
|
13327
13340
|
const locale = options2?.locale || route.locale;
|
|
@@ -13365,7 +13378,7 @@ var Renderer = class {
|
|
|
13365
13378
|
}
|
|
13366
13379
|
res.status(statusCode);
|
|
13367
13380
|
res.set({ "Content-Type": "text/html" });
|
|
13368
|
-
|
|
13381
|
+
setSecurityHeaders(res, {
|
|
13369
13382
|
securityConfig,
|
|
13370
13383
|
nonce
|
|
13371
13384
|
});
|
|
@@ -13493,13 +13506,19 @@ var Renderer = class {
|
|
|
13493
13506
|
return this.rootConfig.jsxRenderer || {};
|
|
13494
13507
|
}
|
|
13495
13508
|
/**
|
|
13496
|
-
*
|
|
13497
|
-
* `preact-render-to-string` depending if the
|
|
13498
|
-
* in `root.config.ts`.
|
|
13509
|
+
* Resolves a synchronous JSX render function, using either the
|
|
13510
|
+
* `@blinkk/root/jsx` package or `preact-render-to-string` depending if the
|
|
13511
|
+
* `jsxRenderer` config is set up in `root.config.ts`.
|
|
13512
|
+
*
|
|
13513
|
+
* Resolving the renderer up front (rather than awaiting inside the render
|
|
13514
|
+
* call) allows callers that temporarily swap global state (e.g. the
|
|
13515
|
+
* `preact.options.vnode` nonce hook) to render synchronously without
|
|
13516
|
+
* yielding to the event loop.
|
|
13499
13517
|
*/
|
|
13500
|
-
async
|
|
13518
|
+
async getJsxRenderFn() {
|
|
13501
13519
|
if (this.rootConfig.jsxRenderer?.mode) {
|
|
13502
|
-
|
|
13520
|
+
const options = this.getJsxRenderOptions();
|
|
13521
|
+
return (vnode) => renderJsxToString(vnode, options);
|
|
13503
13522
|
}
|
|
13504
13523
|
const { renderToString } = await import("preact-render-to-string");
|
|
13505
13524
|
if (!renderToString) {
|
|
@@ -13507,7 +13526,16 @@ var Renderer = class {
|
|
|
13507
13526
|
'failed to render jsx. either install preact-render-to-string or add the "jsxRenderer" config to root.config.ts'
|
|
13508
13527
|
);
|
|
13509
13528
|
}
|
|
13510
|
-
return renderToString(vnode);
|
|
13529
|
+
return (vnode) => renderToString(vnode);
|
|
13530
|
+
}
|
|
13531
|
+
/**
|
|
13532
|
+
* Renders JSX via either the `@blinkk/root/jsx` package or
|
|
13533
|
+
* `preact-render-to-string` depending if the `jsxRenderer` config is set up
|
|
13534
|
+
* in `root.config.ts`.
|
|
13535
|
+
*/
|
|
13536
|
+
async renderJsx(vnode) {
|
|
13537
|
+
const render = await this.getJsxRenderFn();
|
|
13538
|
+
return render(vnode);
|
|
13511
13539
|
}
|
|
13512
13540
|
getConfiguredStyleEntries() {
|
|
13513
13541
|
const styleEntries = this.rootConfig.styles?.entries || [];
|
|
@@ -13679,94 +13707,13 @@ ${content}`;
|
|
|
13679
13707
|
);
|
|
13680
13708
|
return { jsDeps, cssDeps };
|
|
13681
13709
|
}
|
|
13682
|
-
/**
|
|
13683
|
-
* Returns the `security` config value with default values inserted wherever
|
|
13684
|
-
* a user config value is blank or set to `true`.
|
|
13685
|
-
*/
|
|
13686
|
-
getSecurityConfig() {
|
|
13687
|
-
const userConfig = this.rootConfig.server?.security || {};
|
|
13688
|
-
const securityConfig = {};
|
|
13689
|
-
if (isTrueOrUndefined(userConfig.contentSecurityPolicy)) {
|
|
13690
|
-
securityConfig.contentSecurityPolicy = {
|
|
13691
|
-
directives: {
|
|
13692
|
-
"base-uri": ["'none'"],
|
|
13693
|
-
"object-src": ["'none'"],
|
|
13694
|
-
// NOTE: nonce is automatically added to this list.
|
|
13695
|
-
"script-src": [
|
|
13696
|
-
"'unsafe-inline'",
|
|
13697
|
-
"'unsafe-eval'",
|
|
13698
|
-
"'strict-dynamic' https: http:"
|
|
13699
|
-
]
|
|
13700
|
-
},
|
|
13701
|
-
reportOnly: true
|
|
13702
|
-
};
|
|
13703
|
-
} else {
|
|
13704
|
-
securityConfig.contentSecurityPolicy = userConfig.contentSecurityPolicy;
|
|
13705
|
-
}
|
|
13706
|
-
if (isTrueOrUndefined(userConfig.xFrameOptions)) {
|
|
13707
|
-
securityConfig.xFrameOptions = "SAMEORIGIN";
|
|
13708
|
-
} else {
|
|
13709
|
-
securityConfig.xFrameOptions = userConfig.xFrameOptions;
|
|
13710
|
-
}
|
|
13711
|
-
securityConfig.strictTransportSecurity = userConfig.strictTransportSecurity ?? true;
|
|
13712
|
-
securityConfig.xContentTypeOptions = userConfig.xContentTypeOptions ?? true;
|
|
13713
|
-
securityConfig.xXssProtection = userConfig.xXssProtection ?? true;
|
|
13714
|
-
return securityConfig;
|
|
13715
|
-
}
|
|
13716
13710
|
/**
|
|
13717
13711
|
* Generates a random string that can be used as the "nonce" value for CSP.
|
|
13718
13712
|
*/
|
|
13719
13713
|
generateNonce() {
|
|
13720
13714
|
return crypto.randomBytes(16).toString("base64");
|
|
13721
13715
|
}
|
|
13722
|
-
/**
|
|
13723
|
-
* Sets security-related HTTP headers.
|
|
13724
|
-
*/
|
|
13725
|
-
setSecurityHeaders(res, options) {
|
|
13726
|
-
const securityConfig = options.securityConfig;
|
|
13727
|
-
const contentSecurityPolicy = securityConfig.contentSecurityPolicy;
|
|
13728
|
-
if (typeof contentSecurityPolicy === "object") {
|
|
13729
|
-
const directives = { ...contentSecurityPolicy.directives };
|
|
13730
|
-
if (options.nonce) {
|
|
13731
|
-
const scriptSrc = directives["script-src"] || [
|
|
13732
|
-
"'unsafe-inline'",
|
|
13733
|
-
"'unsafe-eval'",
|
|
13734
|
-
"'strict-dynamic' https: http:"
|
|
13735
|
-
];
|
|
13736
|
-
const scriptSrcWithNonce = [...scriptSrc, `'nonce-${options.nonce}'`];
|
|
13737
|
-
directives["script-src"] = scriptSrcWithNonce;
|
|
13738
|
-
}
|
|
13739
|
-
const headerSegments = [];
|
|
13740
|
-
Object.entries(directives).forEach(([key, values]) => {
|
|
13741
|
-
headerSegments.push([key, ...values].join(" "));
|
|
13742
|
-
});
|
|
13743
|
-
const csp = headerSegments.join("; ");
|
|
13744
|
-
if (contentSecurityPolicy.reportOnly === false) {
|
|
13745
|
-
res.setHeader("content-security-policy", csp);
|
|
13746
|
-
} else {
|
|
13747
|
-
res.setHeader("content-security-policy-report-only", csp);
|
|
13748
|
-
}
|
|
13749
|
-
}
|
|
13750
|
-
if (typeof securityConfig.xFrameOptions === "string") {
|
|
13751
|
-
res.setHeader("x-frame-options", securityConfig.xFrameOptions);
|
|
13752
|
-
}
|
|
13753
|
-
if (securityConfig.strictTransportSecurity) {
|
|
13754
|
-
res.setHeader(
|
|
13755
|
-
"strict-transport-security",
|
|
13756
|
-
"max-age=63072000; includeSubdomains; preload"
|
|
13757
|
-
);
|
|
13758
|
-
}
|
|
13759
|
-
if (securityConfig.xContentTypeOptions) {
|
|
13760
|
-
res.setHeader("x-content-type-options", "nosniff");
|
|
13761
|
-
}
|
|
13762
|
-
if (securityConfig.xXssProtection) {
|
|
13763
|
-
res.setHeader("x-xss-protection", "1; mode=block");
|
|
13764
|
-
}
|
|
13765
|
-
}
|
|
13766
13716
|
};
|
|
13767
|
-
function isTrueOrUndefined(value) {
|
|
13768
|
-
return value === true || value === void 0;
|
|
13769
|
-
}
|
|
13770
13717
|
function sortLocales(a, b) {
|
|
13771
13718
|
if (a === "x-default") {
|
|
13772
13719
|
return -1;
|