@taujs/server 0.3.6 → 0.3.7

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.
@@ -1,18 +1,7 @@
1
1
  import { ServerResponse } from 'node:http';
2
- import { FastifyRequest, FastifyReply, HookHandlerDoneFunction, FastifyPluginAsync, FastifyPluginCallback } from 'fastify';
2
+ import { FastifyPluginAsync, FastifyPluginCallback } from 'fastify';
3
3
  import { PluginOption } from 'vite';
4
-
5
- type CSPDirectives = Record<string, string[]>;
6
- interface CSPOptions {
7
- directives?: CSPDirectives;
8
- exposeNonce?: (req: FastifyRequest, nonce: string) => void;
9
- generateCSP?: (directives: CSPDirectives, nonce: string) => string;
10
- }
11
- declare const defaultGenerateCSP: (directives: CSPDirectives, nonce: string) => string;
12
- declare const generateNonce: () => string;
13
- declare const createCSPHook: (options?: CSPOptions) => (req: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void;
14
- declare const getRequestNonce: (req: FastifyRequest) => string | undefined;
15
- declare const applyCSP: (security: SSRServerOptions["security"], reply: FastifyReply) => string | undefined;
4
+ import { CSPDirectives } from './security/csp.js';
16
5
 
17
6
  declare const TEMPLATE: {
18
7
  readonly defaultEntryClient: "entry-client";
@@ -66,7 +55,7 @@ type SSRServerOptions = {
66
55
  security?: {
67
56
  csp?: {
68
57
  directives?: CSPDirectives;
69
- generateCSP?: (directives: CSPDirectives, nonce: string) => string;
58
+ generateCSP?: (directives: CSPDirectives, cspNonce: string) => string;
70
59
  };
71
60
  };
72
61
  registerStaticAssets?: false | {
@@ -101,7 +90,7 @@ type RenderSSR = (initialDataResolved: Record<string, unknown>, location: string
101
90
  headContent: string;
102
91
  appHtml: string;
103
92
  }>;
104
- type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>) => void;
93
+ type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>, cspNonce?: string) => void;
105
94
  type RenderModule = {
106
95
  renderSSR: RenderSSR;
107
96
  renderStream: RenderStream;
@@ -150,4 +139,4 @@ interface InitialRouteParams extends Record<string, unknown> {
150
139
  type RouteParams = InitialRouteParams & Record<string, unknown>;
151
140
  type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
152
141
 
153
- export { type BaseMiddleware as B, type Config as C, type DataResult as D, type GenericPlugin as G, type InitialRouteParams as I, type ManifestEntry as M, type NamedService as N, type ProcessedConfig as P, type Route as R, SSRServer as S, TEMPLATE as T, type RouteParams as a, type RouteAttributes as b, createMaps as c, type SSRServerOptions as d, type ServiceMethod as e, type ServiceRegistry as f, type RenderCallbacks as g, type SSRManifest as h, type Manifest as i, type RenderSSR as j, type RenderStream as k, type RenderModule as l, type ServiceCall as m, type DataHandler as n, type RoutePathsAndAttributes as o, processConfigs as p, type CSPDirectives as q, type CSPOptions as r, defaultGenerateCSP as s, generateNonce as t, createCSPHook as u, getRequestNonce as v, applyCSP as w };
142
+ export { type BaseMiddleware as B, type Config as C, type DataResult as D, type GenericPlugin as G, type InitialRouteParams as I, type ManifestEntry as M, type NamedService as N, type ProcessedConfig as P, type Route as R, SSRServer as S, TEMPLATE as T, type RouteParams as a, type RouteAttributes as b, createMaps as c, type SSRServerOptions as d, type ServiceMethod as e, type ServiceRegistry as f, type RenderCallbacks as g, type SSRManifest as h, type Manifest as i, type RenderSSR as j, type RenderStream as k, type RenderModule as l, type ServiceCall as m, type DataHandler as n, type RoutePathsAndAttributes as o, processConfigs as p };
package/dist/build.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { AppConfig } from './config.js';
2
2
  import 'vite';
3
- import './SSRServer-DPZped7n.js';
3
+ import './SSRServer-CbXIDaoA.js';
4
4
  import 'node:http';
5
5
  import 'fastify';
6
+ import './security/csp.js';
6
7
 
7
8
  /**
8
9
  * taujs [ τjs ] Orchestration System
package/dist/build.js CHANGED
@@ -193,7 +193,7 @@ import { build } from "vite";
193
193
  import { nodePolyfills } from "vite-plugin-node-polyfills";
194
194
 
195
195
  // src/SSRServer.ts
196
- var import_fastify_plugin = __toESM(require_plugin(), 1);
196
+ var import_fastify_plugin2 = __toESM(require_plugin(), 1);
197
197
  var import_picocolors = __toESM(require_picocolors(), 1);
198
198
  import { readFile } from "fs/promises";
199
199
  import path2 from "path";
@@ -266,6 +266,7 @@ function createAuthHook(routes, isDebug) {
266
266
  }
267
267
 
268
268
  // src/security/csp.ts
269
+ var import_fastify_plugin = __toESM(require_plugin(), 1);
269
270
  import crypto from "crypto";
270
271
 
271
272
  // src/utils/Utils.ts
@@ -421,28 +422,22 @@ var defaultGenerateCSP = (directives, nonce) => {
421
422
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
422
423
  };
423
424
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
424
- var createCSPHook = (options = {}) => (req, reply, done) => {
425
- const nonce = generateNonce();
426
- const directives = options.directives ?? DEV_CSP_DIRECTIVES;
427
- const generate = options.generateCSP ?? defaultGenerateCSP;
428
- const cspHeader = generate(directives, nonce);
429
- reply.header("Content-Security-Policy", cspHeader);
430
- if (typeof options.exposeNonce === "function") {
431
- options.exposeNonce(req, nonce);
432
- } else {
433
- req.nonce = nonce;
425
+ var cspPlugin = (0, import_fastify_plugin.default)(
426
+ async (fastify, opts) => {
427
+ fastify.addHook("onRequest", (req, reply, done) => {
428
+ const nonce = generateNonce();
429
+ req.cspNonce = nonce;
430
+ const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
431
+ const generate = opts.generateCSP ?? defaultGenerateCSP;
432
+ const cspHeader = generate(directives, nonce);
433
+ reply.header("Content-Security-Policy", cspHeader);
434
+ done();
435
+ });
436
+ },
437
+ {
438
+ name: "taujs-csp-plugin"
434
439
  }
435
- done();
436
- };
437
- var applyCSP = (security, reply) => {
438
- const nonce = generateNonce();
439
- const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
440
- const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
441
- const header = generate(directives, nonce);
442
- reply.header("Content-Security-Policy", header);
443
- reply.request.nonce = nonce;
444
- return nonce;
445
- };
440
+ );
446
441
 
447
442
  // src/security/verifyMiddleware.ts
448
443
  var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
@@ -489,7 +484,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
489
484
  };
490
485
  });
491
486
  };
492
- var SSRServer = (0, import_fastify_plugin.default)(
487
+ var SSRServer = (0, import_fastify_plugin2.default)(
493
488
  async (app, opts) => {
494
489
  const logger = createLogger(opts.isDebug ?? false);
495
490
  const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
@@ -548,13 +543,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
548
543
  ...options ?? {}
549
544
  });
550
545
  }
551
- app.addHook(
552
- "onRequest",
553
- createCSPHook({
554
- directives: opts.security?.csp?.directives,
555
- generateCSP: opts.security?.csp?.generateCSP
556
- })
557
- );
546
+ app.register(cspPlugin, {
547
+ directives: opts.security?.csp?.directives,
548
+ generateCSP: opts.security?.csp?.generateCSP
549
+ });
558
550
  app.addHook("onRequest", createAuthHook(routes));
559
551
  if (isDevelopment) {
560
552
  const { createServer } = await import("vite");
@@ -613,7 +605,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
613
605
  if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
614
606
  const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
615
607
  const matchedRoute = matchRoute(url, routes);
616
- const nonce = applyCSP(opts.security, reply);
608
+ const cspNonce = req.cspNonce;
617
609
  if (!matchedRoute) {
618
610
  reply.callNotFound();
619
611
  return;
@@ -655,18 +647,23 @@ var SSRServer = (0, import_fastify_plugin.default)(
655
647
  if (renderType === RENDERTYPE.ssr) {
656
648
  const { renderSSR } = renderModule;
657
649
  const initialDataResolved = await initialDataPromise;
658
- const initialDataScript = `<script nonce="${nonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
650
+ const initialDataScript = `<script nonce="${cspNonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
659
651
  const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
660
652
  let aggregateHeadContent = headContent;
661
653
  if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
662
654
  if (manifest && cssLink) aggregateHeadContent += cssLink;
663
655
  const shouldHydrate = attr?.hydrate !== false;
664
- const bootstrapScriptTag = shouldHydrate ? `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
656
+ const bootstrapScriptTag = shouldHydrate ? `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
665
657
  const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
666
658
  return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
667
659
  } else {
668
660
  const { renderStream } = renderModule;
669
- reply.raw.writeHead(200, { "Content-Type": "text/html" });
661
+ const cspNonce2 = req.cspNonce;
662
+ const cspHeader = reply.getHeader("Content-Security-Policy");
663
+ reply.raw.writeHead(200, {
664
+ "Content-Security-Policy": cspHeader,
665
+ "Content-Type": "text/html"
666
+ });
670
667
  renderStream(
671
668
  reply.raw,
672
669
  {
@@ -677,7 +674,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
677
674
  reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
678
675
  },
679
676
  onFinish: async (initialDataResolved) => {
680
- reply.raw.write(`<script nonce="${nonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`);
677
+ reply.raw.write(
678
+ `<script nonce="${cspNonce2}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`
679
+ );
681
680
  reply.raw.write(afterBody);
682
681
  reply.raw.end();
683
682
  },
@@ -689,7 +688,8 @@ var SSRServer = (0, import_fastify_plugin.default)(
689
688
  initialDataPromise,
690
689
  req.url,
691
690
  bootstrapModule,
692
- attr?.meta
691
+ attr?.meta,
692
+ cspNonce2
693
693
  );
694
694
  }
695
695
  } catch (error) {
@@ -704,13 +704,14 @@ var SSRServer = (0, import_fastify_plugin.default)(
704
704
  const defaultConfig = processedConfigs[0];
705
705
  if (!defaultConfig) throw new Error("No default configuration found.");
706
706
  const { clientRoot } = defaultConfig;
707
- const nonce = applyCSP(opts.security, reply);
707
+ const cspNonce = req.cspNonce;
708
708
  let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
709
709
  const cssLink = cssLinks.get(clientRoot);
710
710
  const bootstrapModule = bootstrapModules.get(clientRoot);
711
711
  template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
712
712
  if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
713
- if (bootstrapModule) template = template.replace("</body>", `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
713
+ if (bootstrapModule)
714
+ template = template.replace("</body>", `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
714
715
  reply.status(200).type("text/html").send(template);
715
716
  } catch (error) {
716
717
  console.error("Failed to serve clientHtmlTemplate:", error);
package/dist/config.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { PluginOption } from 'vite';
2
- import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-DPZped7n.js';
2
+ import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-CbXIDaoA.js';
3
3
  import 'node:http';
4
4
  import 'fastify';
5
+ import './security/csp.js';
5
6
 
6
7
  /**
7
8
  * taujs [ τjs ] Orchestration System
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { FastifyReply } from 'fastify';
2
- export { B as BaseMiddleware, C as Config, n as DataHandler, D as DataResult, G as GenericPlugin, I as InitialRouteParams, i as Manifest, M as ManifestEntry, N as NamedService, P as ProcessedConfig, g as RenderCallbacks, l as RenderModule, j as RenderSSR, k as RenderStream, R as Route, b as RouteAttributes, a as RouteParams, o as RoutePathsAndAttributes, h as SSRManifest, S as SSRServer, d as SSRServerOptions, m as ServiceCall, e as ServiceMethod, f as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './SSRServer-DPZped7n.js';
2
+ export { B as BaseMiddleware, C as Config, n as DataHandler, D as DataResult, G as GenericPlugin, I as InitialRouteParams, i as Manifest, M as ManifestEntry, N as NamedService, P as ProcessedConfig, g as RenderCallbacks, l as RenderModule, j as RenderSSR, k as RenderStream, R as Route, b as RouteAttributes, a as RouteParams, o as RoutePathsAndAttributes, h as SSRManifest, S as SSRServer, d as SSRServerOptions, m as ServiceCall, e as ServiceMethod, f as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './SSRServer-CbXIDaoA.js';
3
3
  import 'node:http';
4
4
  import 'vite';
5
+ import './security/csp.js';
5
6
 
6
7
  declare module 'fastify' {
7
8
  interface FastifyRequest {
8
- nonce?: string;
9
+ cspNonce?: string;
9
10
  }
10
11
  interface FastifyInstance {
11
12
  /**
package/dist/index.js CHANGED
@@ -191,7 +191,7 @@ var require_picocolors = __commonJS({
191
191
  import "fastify";
192
192
 
193
193
  // src/SSRServer.ts
194
- var import_fastify_plugin = __toESM(require_plugin(), 1);
194
+ var import_fastify_plugin2 = __toESM(require_plugin(), 1);
195
195
  var import_picocolors = __toESM(require_picocolors(), 1);
196
196
  import { readFile } from "fs/promises";
197
197
  import path2 from "path";
@@ -264,6 +264,7 @@ function createAuthHook(routes, isDebug) {
264
264
  }
265
265
 
266
266
  // src/security/csp.ts
267
+ var import_fastify_plugin = __toESM(require_plugin(), 1);
267
268
  import crypto from "crypto";
268
269
 
269
270
  // src/utils/Utils.ts
@@ -419,28 +420,22 @@ var defaultGenerateCSP = (directives, nonce) => {
419
420
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
420
421
  };
421
422
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
422
- var createCSPHook = (options = {}) => (req, reply, done) => {
423
- const nonce = generateNonce();
424
- const directives = options.directives ?? DEV_CSP_DIRECTIVES;
425
- const generate = options.generateCSP ?? defaultGenerateCSP;
426
- const cspHeader = generate(directives, nonce);
427
- reply.header("Content-Security-Policy", cspHeader);
428
- if (typeof options.exposeNonce === "function") {
429
- options.exposeNonce(req, nonce);
430
- } else {
431
- req.nonce = nonce;
423
+ var cspPlugin = (0, import_fastify_plugin.default)(
424
+ async (fastify, opts) => {
425
+ fastify.addHook("onRequest", (req, reply, done) => {
426
+ const nonce = generateNonce();
427
+ req.cspNonce = nonce;
428
+ const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
429
+ const generate = opts.generateCSP ?? defaultGenerateCSP;
430
+ const cspHeader = generate(directives, nonce);
431
+ reply.header("Content-Security-Policy", cspHeader);
432
+ done();
433
+ });
434
+ },
435
+ {
436
+ name: "taujs-csp-plugin"
432
437
  }
433
- done();
434
- };
435
- var applyCSP = (security, reply) => {
436
- const nonce = generateNonce();
437
- const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
438
- const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
439
- const header = generate(directives, nonce);
440
- reply.header("Content-Security-Policy", header);
441
- reply.request.nonce = nonce;
442
- return nonce;
443
- };
438
+ );
444
439
 
445
440
  // src/security/verifyMiddleware.ts
446
441
  var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
@@ -487,7 +482,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
487
482
  };
488
483
  });
489
484
  };
490
- var SSRServer = (0, import_fastify_plugin.default)(
485
+ var SSRServer = (0, import_fastify_plugin2.default)(
491
486
  async (app, opts) => {
492
487
  const logger = createLogger(opts.isDebug ?? false);
493
488
  const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
@@ -546,13 +541,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
546
541
  ...options ?? {}
547
542
  });
548
543
  }
549
- app.addHook(
550
- "onRequest",
551
- createCSPHook({
552
- directives: opts.security?.csp?.directives,
553
- generateCSP: opts.security?.csp?.generateCSP
554
- })
555
- );
544
+ app.register(cspPlugin, {
545
+ directives: opts.security?.csp?.directives,
546
+ generateCSP: opts.security?.csp?.generateCSP
547
+ });
556
548
  app.addHook("onRequest", createAuthHook(routes));
557
549
  if (isDevelopment) {
558
550
  const { createServer } = await import("vite");
@@ -611,7 +603,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
611
603
  if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
612
604
  const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
613
605
  const matchedRoute = matchRoute(url, routes);
614
- const nonce = applyCSP(opts.security, reply);
606
+ const cspNonce = req.cspNonce;
615
607
  if (!matchedRoute) {
616
608
  reply.callNotFound();
617
609
  return;
@@ -653,18 +645,23 @@ var SSRServer = (0, import_fastify_plugin.default)(
653
645
  if (renderType === RENDERTYPE.ssr) {
654
646
  const { renderSSR } = renderModule;
655
647
  const initialDataResolved = await initialDataPromise;
656
- const initialDataScript = `<script nonce="${nonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
648
+ const initialDataScript = `<script nonce="${cspNonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
657
649
  const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
658
650
  let aggregateHeadContent = headContent;
659
651
  if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
660
652
  if (manifest && cssLink) aggregateHeadContent += cssLink;
661
653
  const shouldHydrate = attr?.hydrate !== false;
662
- const bootstrapScriptTag = shouldHydrate ? `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
654
+ const bootstrapScriptTag = shouldHydrate ? `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
663
655
  const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
664
656
  return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
665
657
  } else {
666
658
  const { renderStream } = renderModule;
667
- reply.raw.writeHead(200, { "Content-Type": "text/html" });
659
+ const cspNonce2 = req.cspNonce;
660
+ const cspHeader = reply.getHeader("Content-Security-Policy");
661
+ reply.raw.writeHead(200, {
662
+ "Content-Security-Policy": cspHeader,
663
+ "Content-Type": "text/html"
664
+ });
668
665
  renderStream(
669
666
  reply.raw,
670
667
  {
@@ -675,7 +672,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
675
672
  reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
676
673
  },
677
674
  onFinish: async (initialDataResolved) => {
678
- reply.raw.write(`<script nonce="${nonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`);
675
+ reply.raw.write(
676
+ `<script nonce="${cspNonce2}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`
677
+ );
679
678
  reply.raw.write(afterBody);
680
679
  reply.raw.end();
681
680
  },
@@ -687,7 +686,8 @@ var SSRServer = (0, import_fastify_plugin.default)(
687
686
  initialDataPromise,
688
687
  req.url,
689
688
  bootstrapModule,
690
- attr?.meta
689
+ attr?.meta,
690
+ cspNonce2
691
691
  );
692
692
  }
693
693
  } catch (error) {
@@ -702,13 +702,14 @@ var SSRServer = (0, import_fastify_plugin.default)(
702
702
  const defaultConfig = processedConfigs[0];
703
703
  if (!defaultConfig) throw new Error("No default configuration found.");
704
704
  const { clientRoot } = defaultConfig;
705
- const nonce = applyCSP(opts.security, reply);
705
+ const cspNonce = req.cspNonce;
706
706
  let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
707
707
  const cssLink = cssLinks.get(clientRoot);
708
708
  const bootstrapModule = bootstrapModules.get(clientRoot);
709
709
  template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
710
710
  if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
711
- if (bootstrapModule) template = template.replace("</body>", `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
711
+ if (bootstrapModule)
712
+ template = template.replace("</body>", `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
712
713
  reply.status(200).type("text/html").send(template);
713
714
  } catch (error) {
714
715
  console.error("Failed to serve clientHtmlTemplate:", error);
@@ -1,4 +1,12 @@
1
- import 'fastify';
2
- export { q as CSPDirectives, r as CSPOptions, w as applyCSP, u as createCSPHook, s as defaultGenerateCSP, t as generateNonce, v as getRequestNonce } from '../SSRServer-DPZped7n.js';
3
- import 'node:http';
4
- import 'vite';
1
+ import { FastifyPluginAsync } from 'fastify';
2
+
3
+ interface CSPPluginOptions {
4
+ directives?: CSPDirectives;
5
+ generateCSP?: (directives: CSPDirectives, nonce: string) => string;
6
+ }
7
+ type CSPDirectives = Record<string, string[]>;
8
+ declare const defaultGenerateCSP: (directives: CSPDirectives, nonce: string) => string;
9
+ declare const generateNonce: () => string;
10
+ declare const cspPlugin: FastifyPluginAsync<CSPPluginOptions>;
11
+
12
+ export { type CSPDirectives, type CSPPluginOptions, cspPlugin, defaultGenerateCSP, generateNonce };
@@ -1,4 +1,121 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // node_modules/fastify-plugin/lib/getPluginName.js
28
+ var require_getPluginName = __commonJS({
29
+ "node_modules/fastify-plugin/lib/getPluginName.js"(exports, module) {
30
+ "use strict";
31
+ var fpStackTracePattern = /at\s{1}(?:.*\.)?plugin\s{1}.*\n\s*(.*)/;
32
+ var fileNamePattern = /(\w*(\.\w*)*)\..*/;
33
+ module.exports = function getPluginName(fn) {
34
+ if (fn.name.length > 0) return fn.name;
35
+ const stackTraceLimit = Error.stackTraceLimit;
36
+ Error.stackTraceLimit = 10;
37
+ try {
38
+ throw new Error("anonymous function");
39
+ } catch (e) {
40
+ Error.stackTraceLimit = stackTraceLimit;
41
+ return extractPluginName(e.stack);
42
+ }
43
+ };
44
+ function extractPluginName(stack) {
45
+ const m = stack.match(fpStackTracePattern);
46
+ return m ? m[1].split(/[/\\]/).slice(-1)[0].match(fileNamePattern)[1] : "anonymous";
47
+ }
48
+ module.exports.extractPluginName = extractPluginName;
49
+ }
50
+ });
51
+
52
+ // node_modules/fastify-plugin/lib/toCamelCase.js
53
+ var require_toCamelCase = __commonJS({
54
+ "node_modules/fastify-plugin/lib/toCamelCase.js"(exports, module) {
55
+ "use strict";
56
+ module.exports = function toCamelCase(name) {
57
+ if (name[0] === "@") {
58
+ name = name.slice(1).replace("/", "-");
59
+ }
60
+ return name.replace(/-(.)/g, function(match2, g1) {
61
+ return g1.toUpperCase();
62
+ });
63
+ };
64
+ }
65
+ });
66
+
67
+ // node_modules/fastify-plugin/plugin.js
68
+ var require_plugin = __commonJS({
69
+ "node_modules/fastify-plugin/plugin.js"(exports, module) {
70
+ "use strict";
71
+ var getPluginName = require_getPluginName();
72
+ var toCamelCase = require_toCamelCase();
73
+ var count = 0;
74
+ function plugin(fn, options = {}) {
75
+ let autoName = false;
76
+ if (fn.default !== void 0) {
77
+ fn = fn.default;
78
+ }
79
+ if (typeof fn !== "function") {
80
+ throw new TypeError(
81
+ `fastify-plugin expects a function, instead got a '${typeof fn}'`
82
+ );
83
+ }
84
+ if (typeof options === "string") {
85
+ options = {
86
+ fastify: options
87
+ };
88
+ }
89
+ if (typeof options !== "object" || Array.isArray(options) || options === null) {
90
+ throw new TypeError("The options object should be an object");
91
+ }
92
+ if (options.fastify !== void 0 && typeof options.fastify !== "string") {
93
+ throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`);
94
+ }
95
+ if (!options.name) {
96
+ autoName = true;
97
+ options.name = getPluginName(fn) + "-auto-" + count++;
98
+ }
99
+ fn[Symbol.for("skip-override")] = options.encapsulate !== true;
100
+ fn[Symbol.for("fastify.display-name")] = options.name;
101
+ fn[Symbol.for("plugin-meta")] = options;
102
+ if (!fn.default) {
103
+ fn.default = fn;
104
+ }
105
+ const camelCase = toCamelCase(options.name);
106
+ if (!autoName && !fn[camelCase]) {
107
+ fn[camelCase] = fn;
108
+ }
109
+ return fn;
110
+ }
111
+ module.exports = plugin;
112
+ module.exports.default = plugin;
113
+ module.exports.fastifyPlugin = plugin;
114
+ }
115
+ });
116
+
1
117
  // src/security/csp.ts
118
+ var import_fastify_plugin = __toESM(require_plugin(), 1);
2
119
  import crypto from "crypto";
3
120
 
4
121
  // src/constants.ts
@@ -35,33 +152,24 @@ var defaultGenerateCSP = (directives, nonce) => {
35
152
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
36
153
  };
37
154
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
38
- var createCSPHook = (options = {}) => (req, reply, done) => {
39
- const nonce = generateNonce();
40
- const directives = options.directives ?? DEV_CSP_DIRECTIVES;
41
- const generate = options.generateCSP ?? defaultGenerateCSP;
42
- const cspHeader = generate(directives, nonce);
43
- reply.header("Content-Security-Policy", cspHeader);
44
- if (typeof options.exposeNonce === "function") {
45
- options.exposeNonce(req, nonce);
46
- } else {
47
- req.nonce = nonce;
155
+ var cspPlugin = (0, import_fastify_plugin.default)(
156
+ async (fastify, opts) => {
157
+ fastify.addHook("onRequest", (req, reply, done) => {
158
+ const nonce = generateNonce();
159
+ req.cspNonce = nonce;
160
+ const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
161
+ const generate = opts.generateCSP ?? defaultGenerateCSP;
162
+ const cspHeader = generate(directives, nonce);
163
+ reply.header("Content-Security-Policy", cspHeader);
164
+ done();
165
+ });
166
+ },
167
+ {
168
+ name: "taujs-csp-plugin"
48
169
  }
49
- done();
50
- };
51
- var getRequestNonce = (req) => req.nonce;
52
- var applyCSP = (security, reply) => {
53
- const nonce = generateNonce();
54
- const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
55
- const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
56
- const header = generate(directives, nonce);
57
- reply.header("Content-Security-Policy", header);
58
- reply.request.nonce = nonce;
59
- return nonce;
60
- };
170
+ );
61
171
  export {
62
- applyCSP,
63
- createCSPHook,
172
+ cspPlugin,
64
173
  defaultGenerateCSP,
65
- generateNonce,
66
- getRequestNonce
174
+ generateNonce
67
175
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taujs/server",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "taujs [ τjs ]",
5
5
  "author": "John Smith | Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
6
6
  "license": "MIT",