@taujs/server 0.2.6 → 0.3.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/LICENSE CHANGED
@@ -1,6 +1,8 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) Aoede Ltd 2024
3
+ taujs [ τjs ] Orchestration System
4
+ Author: John Smith
5
+ Copyright (c) Aoede Ltd 2024-present
4
6
 
5
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
8
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @taujs/server
2
2
 
3
+ This package is part of the taujs [ τjs ] orchestration system, authored by John Smith | Aoede, 2024-present. Attribution is appreciated.
4
+
3
5
  `npm install @taujs/server`
4
6
 
5
7
  `yarn add @taujs/server`
@@ -0,0 +1,150 @@
1
+ import { ServerResponse } from 'node:http';
2
+ import { FastifyRequest, FastifyReply, HookHandlerDoneFunction, FastifyPluginAsync } from 'fastify';
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;
16
+
17
+ declare const TEMPLATE: {
18
+ readonly defaultEntryClient: "entry-client";
19
+ readonly defaultEntryServer: "entry-server";
20
+ readonly defaultHtmlTemplate: "index.html";
21
+ };
22
+
23
+ /**
24
+ * taujs [ τjs ] Orchestration System
25
+ * (c) 2024-present Aoede Ltd
26
+ * Author: John Smith
27
+ *
28
+ * Licensed under the MIT License — attribution appreciated.
29
+ * Part of the taujs [ τjs ] system for declarative, build-time orchestration of microfrontend applications,
30
+ * including SSR, streaming, and middleware composition.
31
+ */
32
+
33
+ declare const createMaps: () => {
34
+ bootstrapModules: Map<string, string>;
35
+ cssLinks: Map<string, string>;
36
+ manifests: Map<string, Manifest>;
37
+ preloadLinks: Map<string, string>;
38
+ renderModules: Map<string, RenderModule>;
39
+ ssrManifests: Map<string, SSRManifest>;
40
+ templates: Map<string, string>;
41
+ };
42
+ declare const processConfigs: (configs: Config[], baseClientRoot: string, templateDefaults: typeof TEMPLATE) => ProcessedConfig[];
43
+ declare const SSRServer: FastifyPluginAsync<SSRServerOptions>;
44
+ type Config = {
45
+ appId: string;
46
+ entryPoint: string;
47
+ entryClient?: string;
48
+ entryServer?: string;
49
+ htmlTemplate?: string;
50
+ };
51
+ type ProcessedConfig = {
52
+ appId: string;
53
+ clientRoot: string;
54
+ entryClient: string;
55
+ entryPoint: string;
56
+ entryServer: string;
57
+ htmlTemplate: string;
58
+ plugins?: PluginOption[];
59
+ };
60
+ type SSRServerOptions = {
61
+ alias?: Record<string, string>;
62
+ clientRoot: string;
63
+ configs: Config[];
64
+ routes: Route<RouteParams>[];
65
+ serviceRegistry: ServiceRegistry;
66
+ security?: {
67
+ csp?: {
68
+ directives?: CSPDirectives;
69
+ generateCSP?: (directives: CSPDirectives, nonce: string) => string;
70
+ };
71
+ };
72
+ isDebug?: boolean;
73
+ };
74
+ type ServiceMethod = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
75
+ type NamedService = Record<string, ServiceMethod>;
76
+ type ServiceRegistry = Record<string, NamedService>;
77
+ type RenderCallbacks = {
78
+ onHead: (headContent: string) => void;
79
+ onFinish: (initialDataResolved: unknown) => void;
80
+ onError: (error: unknown) => void;
81
+ };
82
+ type FetchConfig = {
83
+ url?: string;
84
+ options: RequestInit & {
85
+ params?: Record<string, unknown>;
86
+ };
87
+ serviceName?: string;
88
+ serviceMethod?: string;
89
+ };
90
+ type SSRManifest = {
91
+ [key: string]: string[];
92
+ };
93
+ type ManifestEntry = {
94
+ file: string;
95
+ src?: string;
96
+ isDynamicEntry?: boolean;
97
+ imports?: string[];
98
+ css?: string[];
99
+ assets?: string[];
100
+ };
101
+ type Manifest = {
102
+ [key: string]: ManifestEntry;
103
+ };
104
+ type RenderSSR = (initialDataResolved: Record<string, unknown>, location: string, meta?: Record<string, unknown>) => Promise<{
105
+ headContent: string;
106
+ appHtml: string;
107
+ }>;
108
+ type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>) => void;
109
+ type RenderModule = {
110
+ renderSSR: RenderSSR;
111
+ renderStream: RenderStream;
112
+ };
113
+ type BaseMiddleware = {
114
+ auth?: {
115
+ required: boolean;
116
+ redirect?: string;
117
+ roles?: string[];
118
+ strategy?: string;
119
+ };
120
+ };
121
+ type RouteAttributes<Params = {}, Middleware = BaseMiddleware> = {
122
+ render: 'ssr';
123
+ hydrate?: boolean;
124
+ meta?: Record<string, unknown>;
125
+ middleware?: Middleware;
126
+ fetch?: (params?: Params, options?: RequestInit & {
127
+ params?: Record<string, unknown>;
128
+ }) => Promise<FetchConfig>;
129
+ } | {
130
+ render: 'streaming';
131
+ hydrate?: never;
132
+ meta: Record<string, unknown>;
133
+ middleware?: Middleware;
134
+ fetch?: (params?: Params, options?: RequestInit & {
135
+ params?: Record<string, unknown>;
136
+ }) => Promise<FetchConfig>;
137
+ };
138
+ type Route<Params = {}> = {
139
+ attr?: RouteAttributes<Params>;
140
+ path: string;
141
+ appId?: string;
142
+ };
143
+ interface InitialRouteParams extends Record<string, unknown> {
144
+ serviceName?: string;
145
+ serviceMethod?: string;
146
+ }
147
+ type RouteParams = InitialRouteParams & Record<string, unknown>;
148
+ type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
149
+
150
+ export { type BaseMiddleware as B, type Config as C, type FetchConfig as F, 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 RoutePathsAndAttributes as m, type CSPDirectives as n, type CSPOptions as o, processConfigs as p, defaultGenerateCSP as q, generateNonce as r, createCSPHook as s, getRequestNonce as t, applyCSP as u };
package/dist/build.d.ts CHANGED
@@ -1,15 +1,24 @@
1
- import { PluginOption } from 'vite';
1
+ import { AppConfig } from './config.js';
2
+ import 'vite';
3
+ import './SSRServer-CmMH3qwx.js';
4
+ import 'node:http';
5
+ import 'fastify';
6
+
7
+ /**
8
+ * taujs [ τjs ] Orchestration System
9
+ * (c) 2024-present Aoede Ltd
10
+ * Author: John Smith
11
+ *
12
+ * Licensed under the MIT License — attribution appreciated.
13
+ * Part of the taujs [ τjs ] system for declarative, build-time orchestration of microfrontend applications,
14
+ * including SSR, streaming, and middleware composition.
15
+ */
2
16
 
3
- type Config = {
4
- appId: string;
5
- entryPoint: string;
6
- plugins?: PluginOption[];
7
- };
8
17
  declare function taujsBuild({ configs, projectRoot, clientBaseDir, isSSRBuild, }: {
9
- configs: Config[];
18
+ configs: AppConfig[];
10
19
  projectRoot: string;
11
20
  clientBaseDir: string;
12
21
  isSSRBuild?: boolean;
13
22
  }): Promise<void>;
14
23
 
15
- export { type Config, taujsBuild };
24
+ export { taujsBuild };
package/dist/build.js CHANGED
@@ -198,6 +198,26 @@ var import_picocolors = __toESM(require_picocolors(), 1);
198
198
  import { readFile } from "fs/promises";
199
199
  import path2 from "path";
200
200
 
201
+ // src/utils/Logger.ts
202
+ var createLogger = (debug) => ({
203
+ log: (...args) => {
204
+ if (debug) console.log(...args);
205
+ },
206
+ warn: (...args) => {
207
+ if (debug) console.warn(...args);
208
+ },
209
+ error: (...args) => {
210
+ if (debug) console.error(...args);
211
+ }
212
+ });
213
+ var debugLog = (logger, message, req) => {
214
+ const prefix = "[\u03C4js]";
215
+ const method = req?.method ?? "";
216
+ const url = req?.url ?? "";
217
+ const tag = method && url ? `${method} ${url}` : "";
218
+ logger.log(`${prefix} ${tag} ${message}`);
219
+ };
220
+
201
221
  // src/constants.ts
202
222
  var RENDERTYPE = {
203
223
  ssr: "ssr",
@@ -219,6 +239,32 @@ var DEV_CSP_DIRECTIVES = {
219
239
  "img-src": ["'self'", "data:"]
220
240
  };
221
241
 
242
+ // src/security/auth.ts
243
+ function createAuthHook(routes, isDebug) {
244
+ const logger = createLogger(Boolean(isDebug));
245
+ return async function authHook(req, reply) {
246
+ const url = new URL(req.url, `http://${req.headers.host}`).pathname;
247
+ const matched = routes.find((r) => r.path === url);
248
+ const authConfig = matched?.attr?.middleware?.auth;
249
+ if (!authConfig?.required) {
250
+ debugLog(logger, "Auth not required for route", req);
251
+ return;
252
+ }
253
+ if (typeof req.server.authenticate !== "function") {
254
+ req.log.warn('Route requires auth but no "authenticate" decorator is defined on Fastify.');
255
+ return reply.status(500).send("Server misconfiguration: auth decorator missing.");
256
+ }
257
+ try {
258
+ debugLog(logger, "Invoking authenticate(...)", req);
259
+ await req.server.authenticate(req, reply);
260
+ debugLog(logger, "Authentication successful", req);
261
+ } catch (err) {
262
+ debugLog(logger, "Authentication failed", req);
263
+ return reply.send(err);
264
+ }
265
+ };
266
+ }
267
+
222
268
  // src/security/csp.ts
223
269
  import crypto from "crypto";
224
270
  var defaultGenerateCSP = (directives, nonce) => {
@@ -237,7 +283,7 @@ var defaultGenerateCSP = (directives, nonce) => {
237
283
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
238
284
  };
239
285
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
240
- var cspHook = (options = {}) => (req, reply, done) => {
286
+ var createCSPHook = (options = {}) => (req, reply, done) => {
241
287
  const nonce = generateNonce();
242
288
  const directives = options.directives ?? DEV_CSP_DIRECTIVES;
243
289
  const generate = options.generateCSP ?? defaultGenerateCSP;
@@ -260,6 +306,26 @@ var applyCSP = (security, reply) => {
260
306
  return nonce;
261
307
  };
262
308
 
309
+ // src/security/verifyMiddleware.ts
310
+ var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
311
+ var hasAuthenticate = (app) => typeof app.authenticate === "function";
312
+ var verifyContracts = (app, routes, contracts, isDebug) => {
313
+ const logger = createLogger(Boolean(isDebug));
314
+ for (const contract of contracts) {
315
+ const isUsed = routes.some(contract.required);
316
+ if (!isUsed) {
317
+ debugLog(logger, `Middleware "${contract.key}" not used in any routes`);
318
+ continue;
319
+ }
320
+ if (!contract.verify(app)) {
321
+ const error = new Error(`[\u03C4js] ${contract.errorMessage}`);
322
+ logger.error(error.message);
323
+ throw error;
324
+ }
325
+ debugLog(logger, `Middleware "${contract.key}" verified \u2713`);
326
+ }
327
+ };
328
+
263
329
  // src/utils/Utils.ts
264
330
  import { dirname, join } from "path";
265
331
  import "path";
@@ -330,16 +396,15 @@ function renderPreloadLink(file) {
330
396
  return "";
331
397
  }
332
398
  }
333
- var callServiceMethod = async (serviceRegistry, serviceName, serviceMethod, params) => {
334
- const service = serviceRegistry[serviceName];
335
- if (service && typeof service[serviceMethod] === "function") {
336
- const method = service[serviceMethod];
337
- const data = await method(params);
338
- if (typeof data !== "object" || data === null)
339
- throw new Error(`Expected object response from service method ${serviceMethod} on ${serviceName}, but got ${typeof data}`);
340
- return data;
341
- }
342
- throw new Error(`Service method ${serviceMethod} does not exist on ${serviceName}`);
399
+ var callServiceMethod = async (registry, serviceName, methodName, params) => {
400
+ const service = registry[serviceName];
401
+ if (!service) throw new Error(`Service ${String(serviceName)} does not exist in the registry`);
402
+ const method = service[methodName];
403
+ if (typeof method !== "function") throw new Error(`Service method ${String(methodName)} does not exist on ${String(serviceName)}`);
404
+ const data = await method(params);
405
+ if (typeof data !== "object" || data === null)
406
+ throw new Error(`Expected object response from ${String(serviceName)}.${String(methodName)}, but got ${typeof data}`);
407
+ return data;
343
408
  };
344
409
  var fetchData = async ({ url, options }) => {
345
410
  if (url) {
@@ -357,10 +422,10 @@ var fetchInitialData = async (attr, params, serviceRegistry) => {
357
422
  headers: { "Content-Type": "application/json" },
358
423
  params
359
424
  }).then(async (data) => {
360
- if (data.serviceName && data.serviceMethod) {
425
+ if (data.serviceName && data.serviceMethod && typeof data.serviceName === "string" && typeof data.serviceMethod === "string")
361
426
  return await callServiceMethod(serviceRegistry, data.serviceName, data.serviceMethod, data.options?.params ?? {});
362
- }
363
- return await fetchData(data);
427
+ if (data.url && typeof data.url === "string") return await fetchData(data);
428
+ throw new Error("Invalid fetch configuration: must have either serviceName+serviceMethod or url");
364
429
  }).catch((error) => {
365
430
  console.error("Error fetching initial data:", error);
366
431
  throw error;
@@ -433,6 +498,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
433
498
  };
434
499
  var SSRServer = (0, import_fastify_plugin.default)(
435
500
  async (app, opts) => {
501
+ const logger = createLogger(opts.isDebug ?? false);
436
502
  const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
437
503
  const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
438
504
  const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
@@ -466,6 +532,19 @@ var SSRServer = (0, import_fastify_plugin.default)(
466
532
  }
467
533
  }
468
534
  let viteDevServer;
535
+ verifyContracts(
536
+ app,
537
+ routes,
538
+ [
539
+ {
540
+ key: "auth",
541
+ required: isAuthRequired,
542
+ verify: hasAuthenticate,
543
+ errorMessage: "Routes require auth but Fastify instance is missing `.authenticate` decorator."
544
+ }
545
+ ],
546
+ opts.isDebug
547
+ );
469
548
  await app.register(import("@fastify/static"), {
470
549
  index: false,
471
550
  prefix: "/",
@@ -474,11 +553,12 @@ var SSRServer = (0, import_fastify_plugin.default)(
474
553
  });
475
554
  app.addHook(
476
555
  "onRequest",
477
- cspHook({
556
+ createCSPHook({
478
557
  directives: opts.security?.csp?.directives,
479
558
  generateCSP: opts.security?.csp?.generateCSP
480
559
  })
481
560
  );
561
+ app.addHook("onRequest", createAuthHook(routes));
482
562
  if (isDevelopment) {
483
563
  const { createServer } = await import("vite");
484
564
  viteDevServer = await createServer({
@@ -496,10 +576,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
496
576
  {
497
577
  name: "taujs-development-server-debug-logging",
498
578
  configureServer(server) {
499
- console.log(import_picocolors.default.green("\u03C4js development server debug started."));
579
+ logger.log(import_picocolors.default.green("\u03C4js development server debug started."));
500
580
  server.middlewares.use((req, res, next) => {
501
- console.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
502
- res.on("finish", () => console.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
581
+ logger.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
582
+ res.on("finish", () => logger.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
503
583
  next();
504
584
  });
505
585
  }
@@ -583,7 +663,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
583
663
  let aggregateHeadContent = headContent;
584
664
  if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
585
665
  if (manifest && cssLink) aggregateHeadContent += cssLink;
586
- const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>`);
666
+ const shouldHydrate = attr?.hydrate !== false;
667
+ const bootstrapScriptTag = shouldHydrate ? `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
668
+ const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
587
669
  return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
588
670
  } else {
589
671
  const { renderStream } = renderModule;
@@ -0,0 +1,37 @@
1
+ import { PluginOption } from 'vite';
2
+ import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-CmMH3qwx.js';
3
+ import 'node:http';
4
+ import 'fastify';
5
+
6
+ /**
7
+ * taujs [ τjs ] Orchestration System
8
+ * (c) 2024-present Aoede Ltd
9
+ * Author: John Smith
10
+ *
11
+ * Licensed under the MIT License — attribution appreciated.
12
+ * Part of the taujs [ τjs ] system for declarative, build-time orchestration of microfrontend applications,
13
+ * including SSR, streaming, and middleware composition.
14
+ */
15
+
16
+ type AppRoute = Omit<Route<RouteParams>, 'appId'> & {
17
+ attr?: RouteAttributes;
18
+ };
19
+ type AppConfig = {
20
+ appId: string;
21
+ entryPoint: string;
22
+ plugins?: PluginOption[];
23
+ routes?: AppRoute[];
24
+ };
25
+ type TaujsConfig = {
26
+ apps: AppConfig[];
27
+ };
28
+ declare const extractBuildConfigs: (config: {
29
+ apps: {
30
+ appId: string;
31
+ entryPoint: string;
32
+ plugins?: PluginOption[];
33
+ }[];
34
+ }) => AppConfig[];
35
+ declare function extractRoutes(taujsConfig: TaujsConfig): Route<RouteParams>[];
36
+
37
+ export { type AppConfig, type AppRoute, type TaujsConfig, extractBuildConfigs, extractRoutes };
package/dist/config.js ADDED
@@ -0,0 +1,146 @@
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/picocolors/picocolors.js
28
+ var require_picocolors = __commonJS({
29
+ "node_modules/picocolors/picocolors.js"(exports, module) {
30
+ "use strict";
31
+ var p = process || {};
32
+ var argv = p.argv || [];
33
+ var env = p.env || {};
34
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
35
+ var formatter = (open, close, replace = open) => (input) => {
36
+ let string = "" + input, index = string.indexOf(close, open.length);
37
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
38
+ };
39
+ var replaceClose = (string, close, replace, index) => {
40
+ let result = "", cursor = 0;
41
+ do {
42
+ result += string.substring(cursor, index) + replace;
43
+ cursor = index + close.length;
44
+ index = string.indexOf(close, cursor);
45
+ } while (~index);
46
+ return result + string.substring(cursor);
47
+ };
48
+ var createColors = (enabled = isColorSupported) => {
49
+ let f = enabled ? formatter : () => String;
50
+ return {
51
+ isColorSupported: enabled,
52
+ reset: f("\x1B[0m", "\x1B[0m"),
53
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
54
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
55
+ italic: f("\x1B[3m", "\x1B[23m"),
56
+ underline: f("\x1B[4m", "\x1B[24m"),
57
+ inverse: f("\x1B[7m", "\x1B[27m"),
58
+ hidden: f("\x1B[8m", "\x1B[28m"),
59
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
60
+ black: f("\x1B[30m", "\x1B[39m"),
61
+ red: f("\x1B[31m", "\x1B[39m"),
62
+ green: f("\x1B[32m", "\x1B[39m"),
63
+ yellow: f("\x1B[33m", "\x1B[39m"),
64
+ blue: f("\x1B[34m", "\x1B[39m"),
65
+ magenta: f("\x1B[35m", "\x1B[39m"),
66
+ cyan: f("\x1B[36m", "\x1B[39m"),
67
+ white: f("\x1B[37m", "\x1B[39m"),
68
+ gray: f("\x1B[90m", "\x1B[39m"),
69
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
70
+ bgRed: f("\x1B[41m", "\x1B[49m"),
71
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
72
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
73
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
74
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
75
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
76
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
77
+ blackBright: f("\x1B[90m", "\x1B[39m"),
78
+ redBright: f("\x1B[91m", "\x1B[39m"),
79
+ greenBright: f("\x1B[92m", "\x1B[39m"),
80
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
81
+ blueBright: f("\x1B[94m", "\x1B[39m"),
82
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
83
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
84
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
85
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
86
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
87
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
88
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
89
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
90
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
91
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
92
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
93
+ };
94
+ };
95
+ module.exports = createColors();
96
+ module.exports.createColors = createColors;
97
+ }
98
+ });
99
+
100
+ // src/config.ts
101
+ var import_picocolors = __toESM(require_picocolors(), 1);
102
+ import { performance } from "perf_hooks";
103
+ var extractBuildConfigs = (config) => {
104
+ return config.apps.map(({ appId, entryPoint, plugins }) => ({
105
+ appId,
106
+ entryPoint,
107
+ plugins
108
+ }));
109
+ };
110
+ function extractRoutes(taujsConfig) {
111
+ console.log(import_picocolors.default.bold("Preparing taujs [ \u03C4js ]"));
112
+ const t0 = performance.now();
113
+ try {
114
+ const allRoutes = [];
115
+ const pathTracker = /* @__PURE__ */ new Map();
116
+ let totalRoutes = 0;
117
+ for (const app of taujsConfig.apps) {
118
+ const appRoutes = (app.routes ?? []).map((route) => {
119
+ const fullRoute = { ...route, appId: app.appId };
120
+ if (!pathTracker.has(route.path)) pathTracker.set(route.path, []);
121
+ pathTracker.get(route.path).push(app.appId);
122
+ return fullRoute;
123
+ });
124
+ console.log(import_picocolors.default.gray(` \u2022 ${app.appId}: ${appRoutes.length} route(s)`));
125
+ allRoutes.push(...appRoutes);
126
+ totalRoutes += appRoutes.length;
127
+ }
128
+ for (const [path, appIds] of pathTracker.entries()) {
129
+ if (appIds.length > 1) console.warn(import_picocolors.default.yellow(`\u26A0\uFE0F Route path "${path}" is declared in multiple apps: ${appIds.join(", ")} \u2013 order may affect matching`));
130
+ }
131
+ const sortedRoutes = allRoutes.sort((a, b) => computeScore(b.path) - computeScore(a.path));
132
+ const t1 = performance.now();
133
+ console.log(import_picocolors.default.green(`Prepared ${totalRoutes} route(s) in ${(t1 - t0).toFixed(1)}ms`));
134
+ return sortedRoutes;
135
+ } catch (err) {
136
+ console.log(import_picocolors.default.red("Failed to prepare routes"));
137
+ throw err;
138
+ }
139
+ }
140
+ function computeScore(path) {
141
+ return path.split("/").filter(Boolean).length;
142
+ }
143
+ export {
144
+ extractBuildConfigs,
145
+ extractRoutes
146
+ };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,24 @@
1
- export { C as Config, F as FetchConfig, I as InitialRouteParams, f as Manifest, M as ManifestEntry, N as NamedService, P as ProcessedConfig, R as RenderCallbacks, i as RenderModule, g as RenderSSR, h as RenderStream, k as Route, j as RouteAttributes, l as RouteParams, m as RoutePathsAndAttributes, e as SSRManifest, S as SSRServer, a as SSRServerOptions, b as ServiceMethod, d as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './security/csp.js';
1
+ import { FastifyReply } from 'fastify';
2
+ export { B as BaseMiddleware, C as Config, F as FetchConfig, 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, m as RoutePathsAndAttributes, h as SSRManifest, S as SSRServer, d as SSRServerOptions, e as ServiceMethod, f as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './SSRServer-CmMH3qwx.js';
2
3
  import 'node:http';
3
- import 'fastify';
4
4
  import 'vite';
5
+
6
+ declare module 'fastify' {
7
+ interface FastifyRequest {
8
+ nonce?: string;
9
+ }
10
+ interface FastifyInstance {
11
+ /**
12
+ * Optional authentication hook to be used by the TauJS SSRServer.
13
+ * This method must be decorated by the user when using auth middleware in `taujs.config.ts`.
14
+ *
15
+ * Example usage:
16
+ * ```ts
17
+ * fastify.decorate('authenticate', async function (req, reply) {
18
+ * await req.jwtVerify();
19
+ * });
20
+ * ```
21
+ */
22
+ authenticate: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
23
+ }
24
+ }
package/dist/index.js CHANGED
@@ -187,12 +187,35 @@ var require_picocolors = __commonJS({
187
187
  }
188
188
  });
189
189
 
190
+ // src/types.d.ts
191
+ import "fastify";
192
+
190
193
  // src/SSRServer.ts
191
194
  var import_fastify_plugin = __toESM(require_plugin(), 1);
192
195
  var import_picocolors = __toESM(require_picocolors(), 1);
193
196
  import { readFile } from "fs/promises";
194
197
  import path2 from "path";
195
198
 
199
+ // src/utils/Logger.ts
200
+ var createLogger = (debug) => ({
201
+ log: (...args) => {
202
+ if (debug) console.log(...args);
203
+ },
204
+ warn: (...args) => {
205
+ if (debug) console.warn(...args);
206
+ },
207
+ error: (...args) => {
208
+ if (debug) console.error(...args);
209
+ }
210
+ });
211
+ var debugLog = (logger, message, req) => {
212
+ const prefix = "[\u03C4js]";
213
+ const method = req?.method ?? "";
214
+ const url = req?.url ?? "";
215
+ const tag = method && url ? `${method} ${url}` : "";
216
+ logger.log(`${prefix} ${tag} ${message}`);
217
+ };
218
+
196
219
  // src/constants.ts
197
220
  var RENDERTYPE = {
198
221
  ssr: "ssr",
@@ -214,6 +237,32 @@ var DEV_CSP_DIRECTIVES = {
214
237
  "img-src": ["'self'", "data:"]
215
238
  };
216
239
 
240
+ // src/security/auth.ts
241
+ function createAuthHook(routes, isDebug) {
242
+ const logger = createLogger(Boolean(isDebug));
243
+ return async function authHook(req, reply) {
244
+ const url = new URL(req.url, `http://${req.headers.host}`).pathname;
245
+ const matched = routes.find((r) => r.path === url);
246
+ const authConfig = matched?.attr?.middleware?.auth;
247
+ if (!authConfig?.required) {
248
+ debugLog(logger, "Auth not required for route", req);
249
+ return;
250
+ }
251
+ if (typeof req.server.authenticate !== "function") {
252
+ req.log.warn('Route requires auth but no "authenticate" decorator is defined on Fastify.');
253
+ return reply.status(500).send("Server misconfiguration: auth decorator missing.");
254
+ }
255
+ try {
256
+ debugLog(logger, "Invoking authenticate(...)", req);
257
+ await req.server.authenticate(req, reply);
258
+ debugLog(logger, "Authentication successful", req);
259
+ } catch (err) {
260
+ debugLog(logger, "Authentication failed", req);
261
+ return reply.send(err);
262
+ }
263
+ };
264
+ }
265
+
217
266
  // src/security/csp.ts
218
267
  import crypto from "crypto";
219
268
  var defaultGenerateCSP = (directives, nonce) => {
@@ -232,7 +281,7 @@ var defaultGenerateCSP = (directives, nonce) => {
232
281
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
233
282
  };
234
283
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
235
- var cspHook = (options = {}) => (req, reply, done) => {
284
+ var createCSPHook = (options = {}) => (req, reply, done) => {
236
285
  const nonce = generateNonce();
237
286
  const directives = options.directives ?? DEV_CSP_DIRECTIVES;
238
287
  const generate = options.generateCSP ?? defaultGenerateCSP;
@@ -255,6 +304,26 @@ var applyCSP = (security, reply) => {
255
304
  return nonce;
256
305
  };
257
306
 
307
+ // src/security/verifyMiddleware.ts
308
+ var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
309
+ var hasAuthenticate = (app) => typeof app.authenticate === "function";
310
+ var verifyContracts = (app, routes, contracts, isDebug) => {
311
+ const logger = createLogger(Boolean(isDebug));
312
+ for (const contract of contracts) {
313
+ const isUsed = routes.some(contract.required);
314
+ if (!isUsed) {
315
+ debugLog(logger, `Middleware "${contract.key}" not used in any routes`);
316
+ continue;
317
+ }
318
+ if (!contract.verify(app)) {
319
+ const error = new Error(`[\u03C4js] ${contract.errorMessage}`);
320
+ logger.error(error.message);
321
+ throw error;
322
+ }
323
+ debugLog(logger, `Middleware "${contract.key}" verified \u2713`);
324
+ }
325
+ };
326
+
258
327
  // src/utils/Utils.ts
259
328
  import { dirname, join } from "path";
260
329
  import "path";
@@ -325,16 +394,15 @@ function renderPreloadLink(file) {
325
394
  return "";
326
395
  }
327
396
  }
328
- var callServiceMethod = async (serviceRegistry, serviceName, serviceMethod, params) => {
329
- const service = serviceRegistry[serviceName];
330
- if (service && typeof service[serviceMethod] === "function") {
331
- const method = service[serviceMethod];
332
- const data = await method(params);
333
- if (typeof data !== "object" || data === null)
334
- throw new Error(`Expected object response from service method ${serviceMethod} on ${serviceName}, but got ${typeof data}`);
335
- return data;
336
- }
337
- throw new Error(`Service method ${serviceMethod} does not exist on ${serviceName}`);
397
+ var callServiceMethod = async (registry, serviceName, methodName, params) => {
398
+ const service = registry[serviceName];
399
+ if (!service) throw new Error(`Service ${String(serviceName)} does not exist in the registry`);
400
+ const method = service[methodName];
401
+ if (typeof method !== "function") throw new Error(`Service method ${String(methodName)} does not exist on ${String(serviceName)}`);
402
+ const data = await method(params);
403
+ if (typeof data !== "object" || data === null)
404
+ throw new Error(`Expected object response from ${String(serviceName)}.${String(methodName)}, but got ${typeof data}`);
405
+ return data;
338
406
  };
339
407
  var fetchData = async ({ url, options }) => {
340
408
  if (url) {
@@ -352,10 +420,10 @@ var fetchInitialData = async (attr, params, serviceRegistry) => {
352
420
  headers: { "Content-Type": "application/json" },
353
421
  params
354
422
  }).then(async (data) => {
355
- if (data.serviceName && data.serviceMethod) {
423
+ if (data.serviceName && data.serviceMethod && typeof data.serviceName === "string" && typeof data.serviceMethod === "string")
356
424
  return await callServiceMethod(serviceRegistry, data.serviceName, data.serviceMethod, data.options?.params ?? {});
357
- }
358
- return await fetchData(data);
425
+ if (data.url && typeof data.url === "string") return await fetchData(data);
426
+ throw new Error("Invalid fetch configuration: must have either serviceName+serviceMethod or url");
359
427
  }).catch((error) => {
360
428
  console.error("Error fetching initial data:", error);
361
429
  throw error;
@@ -428,6 +496,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
428
496
  };
429
497
  var SSRServer = (0, import_fastify_plugin.default)(
430
498
  async (app, opts) => {
499
+ const logger = createLogger(opts.isDebug ?? false);
431
500
  const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
432
501
  const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
433
502
  const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
@@ -461,6 +530,19 @@ var SSRServer = (0, import_fastify_plugin.default)(
461
530
  }
462
531
  }
463
532
  let viteDevServer;
533
+ verifyContracts(
534
+ app,
535
+ routes,
536
+ [
537
+ {
538
+ key: "auth",
539
+ required: isAuthRequired,
540
+ verify: hasAuthenticate,
541
+ errorMessage: "Routes require auth but Fastify instance is missing `.authenticate` decorator."
542
+ }
543
+ ],
544
+ opts.isDebug
545
+ );
464
546
  await app.register(import("@fastify/static"), {
465
547
  index: false,
466
548
  prefix: "/",
@@ -469,11 +551,12 @@ var SSRServer = (0, import_fastify_plugin.default)(
469
551
  });
470
552
  app.addHook(
471
553
  "onRequest",
472
- cspHook({
554
+ createCSPHook({
473
555
  directives: opts.security?.csp?.directives,
474
556
  generateCSP: opts.security?.csp?.generateCSP
475
557
  })
476
558
  );
559
+ app.addHook("onRequest", createAuthHook(routes));
477
560
  if (isDevelopment) {
478
561
  const { createServer } = await import("vite");
479
562
  viteDevServer = await createServer({
@@ -491,10 +574,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
491
574
  {
492
575
  name: "taujs-development-server-debug-logging",
493
576
  configureServer(server) {
494
- console.log(import_picocolors.default.green("\u03C4js development server debug started."));
577
+ logger.log(import_picocolors.default.green("\u03C4js development server debug started."));
495
578
  server.middlewares.use((req, res, next) => {
496
- console.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
497
- res.on("finish", () => console.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
579
+ logger.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
580
+ res.on("finish", () => logger.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
498
581
  next();
499
582
  });
500
583
  }
@@ -578,7 +661,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
578
661
  let aggregateHeadContent = headContent;
579
662
  if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
580
663
  if (manifest && cssLink) aggregateHeadContent += cssLink;
581
- const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>`);
664
+ const shouldHydrate = attr?.hydrate !== false;
665
+ const bootstrapScriptTag = shouldHydrate ? `<script nonce="${nonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
666
+ const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
582
667
  return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
583
668
  } else {
584
669
  const { renderStream } = renderModule;
@@ -1,131 +1,4 @@
1
- import { ServerResponse } from 'node:http';
2
- import { FastifyRequest, FastifyReply, HookHandlerDoneFunction, FastifyPluginAsync } from 'fastify';
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 cspHook: (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;
16
-
17
- declare const RENDERTYPE: {
18
- ssr: string;
19
- streaming: string;
20
- };
21
- declare const TEMPLATE: {
22
- defaultEntryClient: string;
23
- defaultEntryServer: string;
24
- defaultHtmlTemplate: string;
25
- };
26
-
27
- declare const createMaps: () => {
28
- bootstrapModules: Map<string, string>;
29
- cssLinks: Map<string, string>;
30
- manifests: Map<string, Manifest>;
31
- preloadLinks: Map<string, string>;
32
- renderModules: Map<string, RenderModule>;
33
- ssrManifests: Map<string, SSRManifest>;
34
- templates: Map<string, string>;
35
- };
36
- declare const processConfigs: (configs: Config[], baseClientRoot: string, templateDefaults: typeof TEMPLATE) => ProcessedConfig[];
37
- declare const SSRServer: FastifyPluginAsync<SSRServerOptions>;
38
- type Config = {
39
- appId: string;
40
- entryPoint: string;
41
- entryClient?: string;
42
- entryServer?: string;
43
- htmlTemplate?: string;
44
- };
45
- type ProcessedConfig = {
46
- appId: string;
47
- clientRoot: string;
48
- entryClient: string;
49
- entryPoint: string;
50
- entryServer: string;
51
- htmlTemplate: string;
52
- plugins?: PluginOption[];
53
- };
54
- type SSRServerOptions = {
55
- alias?: Record<string, string>;
56
- clientRoot: string;
57
- configs: Config[];
58
- routes: Route<RouteParams>[];
59
- serviceRegistry: ServiceRegistry;
60
- security?: {
61
- csp?: {
62
- directives?: CSPDirectives;
63
- generateCSP?: (directives: CSPDirectives, nonce: string) => string;
64
- };
65
- };
66
- isDebug?: boolean;
67
- };
68
- type ServiceMethod = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
69
- type NamedService = Record<string, ServiceMethod>;
70
- type ServiceRegistry = Record<string, NamedService>;
71
- type RenderCallbacks = {
72
- onHead: (headContent: string) => void;
73
- onFinish: (initialDataResolved: unknown) => void;
74
- onError: (error: unknown) => void;
75
- };
76
- type FetchConfig = {
77
- url?: string;
78
- options: RequestInit & {
79
- params?: Record<string, unknown>;
80
- };
81
- serviceName?: string;
82
- serviceMethod?: string;
83
- };
84
- type SSRManifest = {
85
- [key: string]: string[];
86
- };
87
- type ManifestEntry = {
88
- file: string;
89
- src?: string;
90
- isDynamicEntry?: boolean;
91
- imports?: string[];
92
- css?: string[];
93
- assets?: string[];
94
- };
95
- type Manifest = {
96
- [key: string]: ManifestEntry;
97
- };
98
- type RenderSSR = (initialDataResolved: Record<string, unknown>, location: string, meta?: Record<string, unknown>) => Promise<{
99
- headContent: string;
100
- appHtml: string;
101
- initialDataScript: string;
102
- }>;
103
- type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>) => void;
104
- type RenderModule = {
105
- renderSSR: RenderSSR;
106
- renderStream: RenderStream;
107
- };
108
- type RouteAttributes<Params = {}> = {
109
- fetch?: (params?: Params, options?: RequestInit & {
110
- params?: Record<string, unknown>;
111
- }) => Promise<FetchConfig>;
112
- } & ({
113
- render?: typeof RENDERTYPE.ssr;
114
- meta?: Record<string, unknown>;
115
- } | {
116
- render: typeof RENDERTYPE.streaming;
117
- meta: Record<string, unknown>;
118
- });
119
- type Route<Params = {}> = {
120
- attr?: RouteAttributes<Params>;
121
- path: string;
122
- appId?: string;
123
- };
124
- interface InitialRouteParams extends Record<string, unknown> {
125
- serviceName?: string;
126
- serviceMethod?: string;
127
- }
128
- type RouteParams = InitialRouteParams & Record<string, unknown>;
129
- type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
130
-
131
- export { type Config as C, type CSPDirectives, type CSPOptions, type FetchConfig as F, type InitialRouteParams as I, type ManifestEntry as M, type NamedService as N, type ProcessedConfig as P, type RenderCallbacks as R, SSRServer as S, TEMPLATE as T, type SSRServerOptions as a, applyCSP, type ServiceMethod as b, createMaps as c, cspHook, type ServiceRegistry as d, defaultGenerateCSP, type SSRManifest as e, type Manifest as f, type RenderSSR as g, generateNonce, getRequestNonce, type RenderStream as h, type RenderModule as i, type RouteAttributes as j, type Route as k, type RouteParams as l, type RoutePathsAndAttributes as m, processConfigs as p };
1
+ import 'fastify';
2
+ export { n as CSPDirectives, o as CSPOptions, u as applyCSP, s as createCSPHook, q as defaultGenerateCSP, r as generateNonce, t as getRequestNonce } from '../SSRServer-CmMH3qwx.js';
3
+ import 'node:http';
4
+ import 'vite';
@@ -26,7 +26,7 @@ var defaultGenerateCSP = (directives, nonce) => {
26
26
  return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
27
27
  };
28
28
  var generateNonce = () => crypto.randomBytes(16).toString("base64");
29
- var cspHook = (options = {}) => (req, reply, done) => {
29
+ var createCSPHook = (options = {}) => (req, reply, done) => {
30
30
  const nonce = generateNonce();
31
31
  const directives = options.directives ?? DEV_CSP_DIRECTIVES;
32
32
  const generate = options.generateCSP ?? defaultGenerateCSP;
@@ -51,7 +51,7 @@ var applyCSP = (security, reply) => {
51
51
  };
52
52
  export {
53
53
  applyCSP,
54
- cspHook,
54
+ createCSPHook,
55
55
  defaultGenerateCSP,
56
56
  generateNonce,
57
57
  getRequestNonce
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@taujs/server",
3
- "version": "0.2.6",
4
- "description": "taujs | τjs",
5
- "author": "Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
3
+ "version": "0.3.0",
4
+ "description": "taujs [ τjs ]",
5
+ "author": "John Smith | Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/aoede3/taujs-server",
8
8
  "repository": {
@@ -33,6 +33,10 @@
33
33
  "import": "./dist/build.js",
34
34
  "types": "./dist/build.d.ts"
35
35
  },
36
+ "./config": {
37
+ "import": "./dist/config.js",
38
+ "types": "./dist/config.d.ts"
39
+ },
36
40
  "./csp": {
37
41
  "import": "./dist/security/csp.js",
38
42
  "types": "./dist/security/csp.d.ts"