@taujs/server 0.2.7 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -1
- package/README.md +2 -0
- package/dist/SSRServer-C7MMCfVq.d.ts +150 -0
- package/dist/build.d.ts +17 -8
- package/dist/build.js +89 -6
- package/dist/config.d.ts +37 -0
- package/dist/config.js +146 -0
- package/dist/index.d.ts +22 -2
- package/dist/index.js +92 -6
- package/dist/security/csp.d.ts +4 -130
- package/dist/security/csp.js +2 -2
- package/package.json +7 -3
package/LICENSE
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
|
|
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
|
@@ -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 CSR, 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 {
|
|
1
|
+
import { AppConfig } from './config.js';
|
|
2
|
+
import 'vite';
|
|
3
|
+
import './SSRServer-C7MMCfVq.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 CSR, 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:
|
|
18
|
+
configs: AppConfig[];
|
|
10
19
|
projectRoot: string;
|
|
11
20
|
clientBaseDir: string;
|
|
12
21
|
isSSRBuild?: boolean;
|
|
13
22
|
}): Promise<void>;
|
|
14
23
|
|
|
15
|
-
export {
|
|
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
|
|
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";
|
|
@@ -432,6 +498,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
|
|
|
432
498
|
};
|
|
433
499
|
var SSRServer = (0, import_fastify_plugin.default)(
|
|
434
500
|
async (app, opts) => {
|
|
501
|
+
const logger = createLogger(opts.isDebug ?? false);
|
|
435
502
|
const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
|
|
436
503
|
const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
|
|
437
504
|
const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
|
|
@@ -465,6 +532,19 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
465
532
|
}
|
|
466
533
|
}
|
|
467
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
|
+
);
|
|
468
548
|
await app.register(import("@fastify/static"), {
|
|
469
549
|
index: false,
|
|
470
550
|
prefix: "/",
|
|
@@ -473,11 +553,12 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
473
553
|
});
|
|
474
554
|
app.addHook(
|
|
475
555
|
"onRequest",
|
|
476
|
-
|
|
556
|
+
createCSPHook({
|
|
477
557
|
directives: opts.security?.csp?.directives,
|
|
478
558
|
generateCSP: opts.security?.csp?.generateCSP
|
|
479
559
|
})
|
|
480
560
|
);
|
|
561
|
+
app.addHook("onRequest", createAuthHook(routes));
|
|
481
562
|
if (isDevelopment) {
|
|
482
563
|
const { createServer } = await import("vite");
|
|
483
564
|
viteDevServer = await createServer({
|
|
@@ -495,10 +576,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
495
576
|
{
|
|
496
577
|
name: "taujs-development-server-debug-logging",
|
|
497
578
|
configureServer(server) {
|
|
498
|
-
|
|
579
|
+
logger.log(import_picocolors.default.green("\u03C4js development server debug started."));
|
|
499
580
|
server.middlewares.use((req, res, next) => {
|
|
500
|
-
|
|
501
|
-
res.on("finish", () =>
|
|
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}`)));
|
|
502
583
|
next();
|
|
503
584
|
});
|
|
504
585
|
}
|
|
@@ -582,7 +663,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
582
663
|
let aggregateHeadContent = headContent;
|
|
583
664
|
if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
|
|
584
665
|
if (manifest && cssLink) aggregateHeadContent += cssLink;
|
|
585
|
-
const
|
|
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}`);
|
|
586
669
|
return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
|
|
587
670
|
} else {
|
|
588
671
|
const { renderStream } = renderModule;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PluginOption } from 'vite';
|
|
2
|
+
import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-C7MMCfVq.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 CSR, 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 const 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
|
+
var 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
|
+
var computeScore = (path) => {
|
|
141
|
+
return path.split("/").filter(Boolean).reduce((score, segment) => score + (segment.startsWith(":") ? 1 : 10), 0);
|
|
142
|
+
};
|
|
143
|
+
export {
|
|
144
|
+
extractBuildConfigs,
|
|
145
|
+
extractRoutes
|
|
146
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
|
|
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-C7MMCfVq.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
|
|
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";
|
|
@@ -427,6 +496,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
|
|
|
427
496
|
};
|
|
428
497
|
var SSRServer = (0, import_fastify_plugin.default)(
|
|
429
498
|
async (app, opts) => {
|
|
499
|
+
const logger = createLogger(opts.isDebug ?? false);
|
|
430
500
|
const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
|
|
431
501
|
const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
|
|
432
502
|
const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
|
|
@@ -460,6 +530,19 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
460
530
|
}
|
|
461
531
|
}
|
|
462
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
|
+
);
|
|
463
546
|
await app.register(import("@fastify/static"), {
|
|
464
547
|
index: false,
|
|
465
548
|
prefix: "/",
|
|
@@ -468,11 +551,12 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
468
551
|
});
|
|
469
552
|
app.addHook(
|
|
470
553
|
"onRequest",
|
|
471
|
-
|
|
554
|
+
createCSPHook({
|
|
472
555
|
directives: opts.security?.csp?.directives,
|
|
473
556
|
generateCSP: opts.security?.csp?.generateCSP
|
|
474
557
|
})
|
|
475
558
|
);
|
|
559
|
+
app.addHook("onRequest", createAuthHook(routes));
|
|
476
560
|
if (isDevelopment) {
|
|
477
561
|
const { createServer } = await import("vite");
|
|
478
562
|
viteDevServer = await createServer({
|
|
@@ -490,10 +574,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
490
574
|
{
|
|
491
575
|
name: "taujs-development-server-debug-logging",
|
|
492
576
|
configureServer(server) {
|
|
493
|
-
|
|
577
|
+
logger.log(import_picocolors.default.green("\u03C4js development server debug started."));
|
|
494
578
|
server.middlewares.use((req, res, next) => {
|
|
495
|
-
|
|
496
|
-
res.on("finish", () =>
|
|
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}`)));
|
|
497
581
|
next();
|
|
498
582
|
});
|
|
499
583
|
}
|
|
@@ -577,7 +661,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
577
661
|
let aggregateHeadContent = headContent;
|
|
578
662
|
if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
|
|
579
663
|
if (manifest && cssLink) aggregateHeadContent += cssLink;
|
|
580
|
-
const
|
|
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}`);
|
|
581
667
|
return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
|
|
582
668
|
} else {
|
|
583
669
|
const { renderStream } = renderModule;
|
package/dist/security/csp.d.ts
CHANGED
|
@@ -1,130 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
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
|
-
}>;
|
|
102
|
-
type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>) => void;
|
|
103
|
-
type RenderModule = {
|
|
104
|
-
renderSSR: RenderSSR;
|
|
105
|
-
renderStream: RenderStream;
|
|
106
|
-
};
|
|
107
|
-
type RouteAttributes<Params = {}> = {
|
|
108
|
-
fetch?: (params?: Params, options?: RequestInit & {
|
|
109
|
-
params?: Record<string, unknown>;
|
|
110
|
-
}) => Promise<FetchConfig>;
|
|
111
|
-
} & ({
|
|
112
|
-
render?: typeof RENDERTYPE.ssr;
|
|
113
|
-
meta?: Record<string, unknown>;
|
|
114
|
-
} | {
|
|
115
|
-
render: typeof RENDERTYPE.streaming;
|
|
116
|
-
meta: Record<string, unknown>;
|
|
117
|
-
});
|
|
118
|
-
type Route<Params = {}> = {
|
|
119
|
-
attr?: RouteAttributes<Params>;
|
|
120
|
-
path: string;
|
|
121
|
-
appId?: string;
|
|
122
|
-
};
|
|
123
|
-
interface InitialRouteParams extends Record<string, unknown> {
|
|
124
|
-
serviceName?: string;
|
|
125
|
-
serviceMethod?: string;
|
|
126
|
-
}
|
|
127
|
-
type RouteParams = InitialRouteParams & Record<string, unknown>;
|
|
128
|
-
type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
|
|
129
|
-
|
|
130
|
-
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-C7MMCfVq.js';
|
|
3
|
+
import 'node:http';
|
|
4
|
+
import 'vite';
|
package/dist/security/csp.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "taujs
|
|
5
|
-
"author": "Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
|
|
3
|
+
"version": "0.3.1",
|
|
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"
|