@anansi/core 0.9.0 → 0.11.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/client.js +31 -22
  3. package/dist/client.js.map +1 -1
  4. package/dist/server.js +205 -30
  5. package/dist/server.js.map +1 -1
  6. package/lib/index.d.ts +1 -0
  7. package/lib/index.d.ts.map +1 -1
  8. package/lib/index.js +6 -2
  9. package/lib/index.server.d.ts +1 -0
  10. package/lib/index.server.d.ts.map +1 -1
  11. package/lib/index.server.js +6 -2
  12. package/lib/laySpouts.d.ts.map +1 -1
  13. package/lib/laySpouts.js +11 -2
  14. package/lib/scripts/startDevserver.d.ts.map +1 -1
  15. package/lib/scripts/startDevserver.js +34 -20
  16. package/lib/spouts/DocumentComponent.d.ts +6 -1
  17. package/lib/spouts/DocumentComponent.d.ts.map +1 -1
  18. package/lib/spouts/DocumentComponent.js +32 -9
  19. package/lib/spouts/csp.d.ts +5 -0
  20. package/lib/spouts/csp.d.ts.map +1 -0
  21. package/lib/spouts/csp.js +20 -0
  22. package/lib/spouts/document.d.ts +1 -1
  23. package/lib/spouts/document.d.ts.map +1 -1
  24. package/lib/spouts/document.js +3 -3
  25. package/lib/spouts/document.server.d.ts +4 -1
  26. package/lib/spouts/document.server.d.ts.map +1 -1
  27. package/lib/spouts/document.server.js +5 -2
  28. package/lib/spouts/json.d.ts +5 -0
  29. package/lib/spouts/json.d.ts.map +1 -0
  30. package/lib/spouts/json.js +22 -0
  31. package/lib/spouts/json.server.d.ts +13 -0
  32. package/lib/spouts/json.server.d.ts.map +1 -0
  33. package/lib/spouts/json.server.js +74 -0
  34. package/lib/spouts/restHooks.d.ts +1 -1
  35. package/lib/spouts/restHooks.d.ts.map +1 -1
  36. package/lib/spouts/restHooks.js +5 -9
  37. package/lib/spouts/restHooks.server.d.ts +41 -1
  38. package/lib/spouts/restHooks.server.d.ts.map +1 -1
  39. package/lib/spouts/restHooks.server.js +6 -3
  40. package/lib/spouts/rhHelp.d.ts +40 -0
  41. package/lib/spouts/rhHelp.d.ts.map +1 -0
  42. package/lib/spouts/rhHelp.js +47 -0
  43. package/lib/spouts/router.d.ts +3 -1
  44. package/lib/spouts/router.d.ts.map +1 -1
  45. package/lib/spouts/router.js +5 -4
  46. package/lib/spouts/types.d.ts +1 -0
  47. package/lib/spouts/types.d.ts.map +1 -1
  48. package/lib/spouts/types.js +1 -1
  49. package/package.json +9 -8
  50. package/src/index.server.ts +1 -0
  51. package/src/index.ts +1 -0
  52. package/src/laySpouts.tsx +5 -1
  53. package/src/scripts/startDevserver.ts +33 -20
  54. package/src/spouts/DocumentComponent.tsx +29 -6
  55. package/src/spouts/csp.ts +25 -0
  56. package/src/spouts/document.server.tsx +7 -1
  57. package/src/spouts/document.tsx +5 -3
  58. package/src/spouts/json.server.tsx +77 -0
  59. package/src/spouts/json.tsx +25 -0
  60. package/src/spouts/restHooks.server.tsx +7 -3
  61. package/src/spouts/restHooks.tsx +12 -7
  62. package/src/spouts/rhHelp.tsx +37 -0
  63. package/src/spouts/router.tsx +12 -4
  64. package/src/spouts/types.ts +1 -0
@@ -3,3 +3,4 @@ export { default as documentSpout } from './spouts/document.server';
3
3
  export { default as restHooksSpout } from './spouts/restHooks.server';
4
4
  export { default as routerSpout } from './spouts/router.server';
5
5
  export { default as prefetchSpout } from './spouts/prefetch.server';
6
+ export { default as JSONSpout } from './spouts/json.server';
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export { default as floodSpouts } from './floodSpouts';
2
2
  export { default as documentSpout } from './spouts/document';
3
3
  export { default as restHooksSpout } from './spouts/restHooks';
4
4
  export { default as routerSpout } from './spouts/router';
5
+ export { default as JSONSpout } from './spouts/json';
package/src/laySpouts.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import { renderToPipeableStream as reactRender } from 'react-dom/server';
2
+ import crypto from 'crypto';
2
3
 
3
4
  import { Render } from './scripts/types';
4
5
  import { ServerProps } from './spouts/types';
@@ -10,7 +11,9 @@ export default function laySpouts(
10
11
  { timeoutMS = 200 }: { timeoutMS?: number } = {},
11
12
  ) {
12
13
  const render: Render = async (clientManifest, req, res) => {
13
- const { app } = await spouts({ clientManifest, req, res });
14
+ const nonce = crypto.randomBytes(16).toString('base64');
15
+
16
+ const { app } = await spouts({ clientManifest, req, res, nonce });
14
17
  let didError = false;
15
18
  const { pipe, abort } = reactRender(
16
19
  app,
@@ -31,6 +34,7 @@ type Options = {|
31
34
  |};
32
35
  */
33
36
  {
37
+ nonce,
34
38
  //bootstrapScripts: assets.filter(asset => asset.endsWith('.js')),
35
39
  onShellReady() {
36
40
  //managers.forEach(manager => manager.cleanup());
@@ -7,13 +7,12 @@ import webpack, { MultiCompiler } from 'webpack';
7
7
  import { createFsFromVolume, Volume } from 'memfs';
8
8
  import { Server, IncomingMessage, ServerResponse } from 'http';
9
9
  import type { NextFunction } from 'express';
10
- import { patchRequire } from 'fs-monkey';
11
10
  import tmp from 'tmp';
12
11
  import sourceMapSupport from 'source-map-support';
13
12
  import { ufs } from 'unionfs';
14
13
  import WebpackDevServer from 'webpack-dev-server';
15
- import importFresh from 'import-fresh';
16
14
  import logging from 'webpack/lib/logging/runtime';
15
+ import { createFsRequire } from 'fs-require';
17
16
 
18
17
  import 'cross-fetch/polyfill';
19
18
  import { BoundRender } from './types';
@@ -47,7 +46,7 @@ export default function startDevServer(
47
46
  const fs = createFsFromVolume(volume);
48
47
  ufs.use(diskFs).use(fs as any);
49
48
 
50
- patchRequire(ufs);
49
+ const fsRequire = createFsRequire(ufs);
51
50
  const readFile = promisify(ufs.readFile);
52
51
  let server: Server | undefined;
53
52
 
@@ -90,10 +89,7 @@ export default function startDevServer(
90
89
  { mode: 'development', target: 'node' },
91
90
  ),
92
91
  ] as const;
93
- // only have one output for server so we can avoid cached modules
94
- webpackConfigs[1].plugins.push(
95
- new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
96
- );
92
+
97
93
  // initialize the webpack compiler
98
94
  const compiler: MultiCompiler = webpack(webpackConfigs);
99
95
 
@@ -122,12 +118,12 @@ export default function startDevServer(
122
118
  };
123
119
  }
124
120
 
125
- const initRender:
121
+ let initRender:
126
122
  | { args: Parameters<BoundRender>; resolve: () => void }[]
127
123
  | undefined = [];
128
124
  let render: BoundRender = (...args) =>
129
125
  new Promise(resolve => {
130
- initRender.push({ args, resolve });
126
+ initRender?.push({ args, resolve });
131
127
  });
132
128
 
133
129
  function importRender(stats: webpack.Stats[]) {
@@ -147,19 +143,29 @@ export default function startDevServer(
147
143
  // ASSETS
148
144
  const clientManifest = clientStats.toJson();
149
145
 
146
+ const serverEntry = getServerBundle(serverStats);
147
+ // reload modules
148
+ Object.keys(fsRequire.cache).forEach(key => {
149
+ delete fsRequire.cache[key];
150
+ });
151
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
152
+ render = (fsRequire(serverEntry) as any).default.bind(
153
+ undefined,
154
+ clientManifest,
155
+ );
150
156
  // SERVER SIDE ENTRYPOINT
151
157
  if (Array.isArray(initRender)) {
152
- // eslint-disable-next-line @typescript-eslint/no-var-requires
153
- render = (require(getServerBundle(serverStats)) as any).default.bind(
154
- undefined,
155
- clientManifest,
156
- );
157
- initRender.forEach(init => render(...init.args).then(init.resolve));
158
- } else {
159
- render = (importFresh(getServerBundle(serverStats)) as any).default.bind(
160
- undefined,
161
- clientManifest,
162
- );
158
+ initRender.forEach(async init => {
159
+ try {
160
+ log.info('Resolving queued requests');
161
+ await render(...init.args);
162
+ init.resolve();
163
+ } catch (e) {
164
+ log.error('Error when attempting to render queued requests');
165
+ log.error(e);
166
+ }
167
+ });
168
+ initRender = undefined;
163
169
  }
164
170
  }
165
171
 
@@ -201,6 +207,13 @@ export default function startDevServer(
201
207
  }),
202
208
  );
203
209
 
210
+ if (webpackConfigs[0].devServer?.setupMiddlewares) {
211
+ return webpackConfigs[0].devServer.setupMiddlewares(
212
+ middlewares,
213
+ devServer,
214
+ );
215
+ }
216
+
204
217
  return middlewares;
205
218
  },
206
219
  },
@@ -1,10 +1,16 @@
1
+ import type { Policy } from './csp';
2
+ import { buildPolicy } from './csp';
3
+
1
4
  type Props = {
2
5
  children: React.ReactNode;
3
6
  assets: { href: string; as?: string; rel?: string }[];
4
7
  head: React.ReactNode;
8
+ scripts: React.ReactNode;
5
9
  title: string;
6
10
  rootId: string;
7
11
  charSet: string;
12
+ csPolicy?: Policy;
13
+ nonce?: string | undefined;
8
14
  };
9
15
 
10
16
  export default function Document({
@@ -14,11 +20,32 @@ export default function Document({
14
20
  title,
15
21
  rootId,
16
22
  charSet,
23
+ csPolicy,
24
+ nonce,
25
+ scripts,
17
26
  }: Props) {
27
+ let cspMeta: null | React.ReactNode = null;
28
+ if (csPolicy) {
29
+ // add nonce to policy
30
+ const policy = {
31
+ ...csPolicy,
32
+ };
33
+ if (nonce) {
34
+ if (typeof policy['script-src'] === 'string') {
35
+ policy['script-src'] = [policy['script-src'], `'nonce-${nonce}'`];
36
+ } else {
37
+ policy['script-src'] = [...policy['script-src'], `'nonce-${nonce}'`];
38
+ }
39
+ }
40
+ cspMeta = (
41
+ <meta httpEquiv="Content-Security-Policy" content={buildPolicy(policy)} />
42
+ );
43
+ }
18
44
  return (
19
45
  <html>
20
46
  <head>
21
47
  <meta charSet={charSet} />
48
+ {cspMeta}
22
49
  {head}
23
50
  {assets.map((asset, i) => (
24
51
  <link key={i} rel="preload" {...asset} />
@@ -27,12 +54,7 @@ export default function Document({
27
54
  </head>
28
55
  <body>
29
56
  <div id={rootId}>{children}</div>
30
- {/* this ensures the client can hydrate the assets prop */}
31
- <script
32
- dangerouslySetInnerHTML={{
33
- __html: `assetManifest = ${JSON.stringify(assets)};`,
34
- }}
35
- />
57
+ {scripts}
36
58
  {assets
37
59
  .filter(({ href }) => href.endsWith('.js'))
38
60
  .map(({ href }, i) => (
@@ -54,4 +76,5 @@ Document.defaultProps = {
54
76
  ),
55
77
  charSet: 'utf-8',
56
78
  rootId: 'anansi-root',
79
+ scripts: null,
57
80
  };
@@ -0,0 +1,25 @@
1
+ export interface Policy {
2
+ [directive: string]: string | string[];
3
+ }
4
+
5
+ // TODO: memoize this
6
+ export function buildPolicy(policyObj: Policy) {
7
+ return Object.keys(policyObj)
8
+ .map(key => {
9
+ const val = Array.isArray(policyObj[key])
10
+ ? [...new Set(policyObj[key]).values()].filter(v => v).join(' ')
11
+ : policyObj[key];
12
+
13
+ // move strict dynamic to the end of the policy if it exists to be backwards compatible with csp2
14
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic
15
+ if (typeof val === 'string' && val.includes("'strict-dynamic'")) {
16
+ const newVal = `${val
17
+ .replace(/\s?'strict-dynamic'\s?/gi, ' ')
18
+ .trim()} 'strict-dynamic'`;
19
+ return `${key} ${newVal}`;
20
+ }
21
+
22
+ return `${key} ${val}`;
23
+ })
24
+ .join('; ');
25
+ }
@@ -3,18 +3,21 @@ import type { Route } from '@anansi/router';
3
3
  import { StatsChunkGroup } from 'webpack';
4
4
 
5
5
  import type { ServerProps, ResolveProps } from './types';
6
+ import type { Policy } from './csp';
6
7
  import Document from './DocumentComponent';
7
8
 
8
9
  type NeededProps = {
9
10
  matchedRoutes: Route<any>[];
10
11
  title?: string;
12
+ scripts?: React.ReactNode[];
11
13
  } & ResolveProps;
12
14
 
13
15
  export default function DocumentSpout(options: {
14
16
  head?: React.ReactNode;
15
17
  title: string;
16
- rootId: string;
18
+ rootId?: string;
17
19
  charSet?: string;
20
+ csPolicy?: Policy;
18
21
  }) {
19
22
  return function <T extends NeededProps>(
20
23
  next: (props: ServerProps) => Promise<T>,
@@ -78,6 +81,9 @@ export default function DocumentSpout(options: {
78
81
  title={nextProps.title ?? options.title}
79
82
  assets={assets}
80
83
  rootId={options.rootId}
84
+ nonce={props.nonce}
85
+ csPolicy={options.csPolicy}
86
+ scripts={nextProps.scripts}
81
87
  >
82
88
  {nextProps.app}
83
89
  </Document>
@@ -12,9 +12,11 @@ export default function documentSpout(options: {
12
12
  head?: React.ReactNode;
13
13
  title: string;
14
14
  }) {
15
- return function <T extends NeededProps>(next: () => Promise<T>) {
16
- return async () => {
17
- const nextProps = await next();
15
+ return function <T extends NeededProps>(
16
+ next: (initData: Record<string, unknown>) => Promise<T>,
17
+ ) {
18
+ return async (initData: Record<string, unknown>) => {
19
+ const nextProps = await next(initData);
18
20
 
19
21
  return nextProps;
20
22
  };
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import type { Route } from '@anansi/router';
3
+ import { StatsChunkGroup } from 'webpack';
4
+
5
+ import type { ServerProps, ResolveProps } from './types';
6
+ import type { Policy } from './csp';
7
+ import Document from './DocumentComponent';
8
+
9
+ type NeededProps = {
10
+ initData?: Record<string, () => unknown>;
11
+ scripts?: React.ReactNode[];
12
+ } & ResolveProps;
13
+
14
+ export default function JSONSpout({
15
+ id = 'anansi-json',
16
+ }: { id?: string } = {}) {
17
+ return function <T extends NeededProps>(
18
+ next: (props: ServerProps) => Promise<T>,
19
+ ) {
20
+ return async (props: ServerProps) => {
21
+ const nextProps = await next(props);
22
+
23
+ const scripts: React.ReactNode[] = nextProps.scripts ?? [];
24
+ /*
25
+ Object.entries(nextProps.initData ?? {}).forEach(([key, data]) => {
26
+ try {
27
+ const encoded = JSON.stringify(data);
28
+ scripts.push(
29
+ <script
30
+ key={key}
31
+ id={`${id}-${key}`}
32
+ type="application/json"
33
+ dangerouslySetInnerHTML={{
34
+ __html: encoded,
35
+ }}
36
+ nonce={props.nonce}
37
+ />,
38
+ );
39
+ } catch (e) {
40
+ // TODO: Use unified logging
41
+ console.error(e);
42
+ }
43
+ });*/
44
+ const Script = () => {
45
+ try {
46
+ const data: any = {};
47
+ Object.entries(nextProps.initData ?? {}).forEach(([key, getData]) => {
48
+ data[key] = getData();
49
+ });
50
+ const encoded = JSON.stringify(data);
51
+ return (
52
+ <script
53
+ key={id}
54
+ id={id}
55
+ type="application/json"
56
+ dangerouslySetInnerHTML={{
57
+ __html: encoded,
58
+ }}
59
+ nonce={props.nonce}
60
+ />
61
+ );
62
+ } catch (e) {
63
+ // TODO: Use unified logging
64
+ console.error('Error serializing json');
65
+ console.error(e);
66
+ return null;
67
+ }
68
+ };
69
+ scripts.push(<Script />);
70
+
71
+ return {
72
+ ...nextProps,
73
+ scripts,
74
+ };
75
+ };
76
+ };
77
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import type { Route } from '@anansi/router';
3
+
4
+ import type { ResolveProps } from './types';
5
+
6
+ type NeededProps = ResolveProps;
7
+
8
+ export default function JSONSpout({
9
+ id = 'anansi-json',
10
+ }: { id?: string } = {}) {
11
+ return function <T extends NeededProps>(
12
+ next: (initData: Record<string, unknown>) => Promise<T>,
13
+ ) {
14
+ return async () => {
15
+ const initData = getDatafromDOM(id);
16
+ const nextProps = await next(initData);
17
+
18
+ return nextProps;
19
+ };
20
+ };
21
+ }
22
+ function getDatafromDOM(id: string): Record<string, unknown> {
23
+ const element = document.querySelector(`#${id}`);
24
+ return element?.innerHTML ? JSON.parse(element?.innerHTML) : undefined;
25
+ }
@@ -1,9 +1,9 @@
1
1
  import { Manager, NetworkManager } from '@rest-hooks/core';
2
- import { createPersistedStore } from '@rest-hooks/ssr';
3
2
 
3
+ import { createPersistedStore } from './rhHelp';
4
4
  import type { ResolveProps, ServerProps } from './types';
5
5
 
6
- type NeededProps = ResolveProps;
6
+ type NeededProps = { initData?: Record<string, () => unknown> } & ResolveProps;
7
7
 
8
8
  export default function restHooksSpout(
9
9
  options: {
@@ -14,7 +14,7 @@ export default function restHooksSpout(
14
14
  next: (props: ServerProps) => Promise<T>,
15
15
  ) {
16
16
  return async (props: ServerProps) => {
17
- const [ServerCacheProvider, controller] = createPersistedStore(
17
+ const [ServerCacheProvider, controller, store] = createPersistedStore(
18
18
  options.getManagers(),
19
19
  );
20
20
 
@@ -23,6 +23,10 @@ export default function restHooksSpout(
23
23
  return {
24
24
  ...nextProps,
25
25
  controller,
26
+ initData: {
27
+ ...nextProps.initData,
28
+ resthooks: () => store.getState(),
29
+ },
26
30
  app: <ServerCacheProvider>{nextProps.app}</ServerCacheProvider>,
27
31
  };
28
32
  };
@@ -1,5 +1,9 @@
1
- import { CacheProvider, Manager, NetworkManager } from '@rest-hooks/core';
2
- import { ServerDataComponent, getDatafromDOM } from '@rest-hooks/ssr';
1
+ import {
2
+ CacheProvider,
3
+ Manager,
4
+ NetworkManager,
5
+ State,
6
+ } from '@rest-hooks/core';
3
7
 
4
8
  import type { ResolveProps } from './types';
5
9
 
@@ -10,18 +14,19 @@ export default function restHooksSpout(
10
14
  getManagers: () => Manager[];
11
15
  } = { getManagers: () => [new NetworkManager()] },
12
16
  ) {
13
- return function <T extends NeededProps>(next: () => Promise<T>) {
14
- return async () => {
15
- const data = getDatafromDOM();
17
+ return function <T extends NeededProps>(
18
+ next: (initData: Record<string, unknown>) => Promise<T>,
19
+ ) {
20
+ return async (initData: Record<string, unknown>) => {
21
+ const data = initData.resthooks as State<unknown>;
16
22
 
17
- const nextProps = await next();
23
+ const nextProps = await next(initData);
18
24
 
19
25
  return {
20
26
  ...nextProps,
21
27
  app: (
22
28
  <CacheProvider initialState={data} managers={options.getManagers()}>
23
29
  {nextProps.app}
24
- <ServerDataComponent data={data} />
25
30
  </CacheProvider>
26
31
  ),
27
32
  };
@@ -0,0 +1,37 @@
1
+ import { ExternalCacheProvider, PromiseifyMiddleware } from 'rest-hooks';
2
+ import {
3
+ Controller,
4
+ createReducer,
5
+ initialState,
6
+ Manager,
7
+ applyManager,
8
+ NetworkManager,
9
+ } from '@rest-hooks/core';
10
+ import { createStore, applyMiddleware } from 'redux';
11
+
12
+ // TODO: Rework this and upstream to rest hooks
13
+ export function createPersistedStore(managers?: Manager[]) {
14
+ const controller = new Controller();
15
+ managers = managers ?? [new NetworkManager()];
16
+ const reducer = createReducer(controller);
17
+ const enhancer = applyMiddleware(
18
+ ...applyManager(managers, controller),
19
+ PromiseifyMiddleware as any,
20
+ );
21
+ const store = createStore(reducer, initialState as any, enhancer);
22
+ managers.forEach(manager => manager.init?.(store.getState()));
23
+
24
+ const selector = (state: any) => state;
25
+ function ServerCacheProvider({ children }: { children: React.ReactNode }) {
26
+ return (
27
+ <ExternalCacheProvider
28
+ store={store}
29
+ selector={selector}
30
+ controller={controller}
31
+ >
32
+ {children}
33
+ </ExternalCacheProvider>
34
+ );
35
+ }
36
+ return [ServerCacheProvider, controller, store] as const;
37
+ }
@@ -1,6 +1,7 @@
1
1
  import { Route, RouteProvider, RouteController } from '@anansi/router';
2
2
  import React from 'react';
3
3
  import { createBrowserHistory } from 'history';
4
+ import type { Update } from 'history';
4
5
 
5
6
  import type { ResolveProps, CreateRouter } from './types';
6
7
 
@@ -10,6 +11,7 @@ export default function routerSpout<ResolveWith>(options: {
10
11
  resolveWith?: any;
11
12
  useResolveWith: () => ResolveWith;
12
13
  createRouter: CreateRouter<ResolveWith>;
14
+ onChange?: (update: Update, callback: () => void | undefined) => void;
13
15
  }) {
14
16
  const createRouteComponent = (
15
17
  router: RouteController<Route<ResolveWith, any>>,
@@ -18,19 +20,25 @@ export default function routerSpout<ResolveWith>(options: {
18
20
  const resolveWith = options.useResolveWith();
19
21
 
20
22
  return (
21
- <RouteProvider router={router} resolveWith={resolveWith}>
23
+ <RouteProvider
24
+ router={router}
25
+ resolveWith={resolveWith}
26
+ onChange={options.onChange}
27
+ >
22
28
  {children}
23
29
  </RouteProvider>
24
30
  );
25
31
  };
26
32
 
27
- return function <T extends NeededProps>(next: () => Promise<T>) {
28
- return async () => {
33
+ return function <T extends NeededProps>(
34
+ next: (initData: Record<string, unknown>) => Promise<T>,
35
+ ) {
36
+ return async (initData: Record<string, unknown>) => {
29
37
  const history = createBrowserHistory();
30
38
  const router = options.createRouter(history);
31
39
  const matchedRoutes = router.getMatchedRoutes(history.location.pathname);
32
40
 
33
- const nextProps = await next();
41
+ const nextProps = await next(initData);
34
42
 
35
43
  const Router = createRouteComponent(router);
36
44
  return {
@@ -9,6 +9,7 @@ export type ServerProps = {
9
9
  req: Request | IncomingMessage;
10
10
  res: Response | ServerResponse;
11
11
  clientManifest: StatsCompilation;
12
+ nonce: string;
12
13
  };
13
14
 
14
15
  /* Baseline expectations of return value */