@taujs/server 0.3.7 → 0.4.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/dist/build.js DELETED
@@ -1,805 +0,0 @@
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
-
117
- // node_modules/picocolors/picocolors.js
118
- var require_picocolors = __commonJS({
119
- "node_modules/picocolors/picocolors.js"(exports, module) {
120
- "use strict";
121
- var p = process || {};
122
- var argv = p.argv || [];
123
- var env = p.env || {};
124
- 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);
125
- var formatter = (open, close, replace = open) => (input) => {
126
- let string = "" + input, index = string.indexOf(close, open.length);
127
- return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
128
- };
129
- var replaceClose = (string, close, replace, index) => {
130
- let result = "", cursor = 0;
131
- do {
132
- result += string.substring(cursor, index) + replace;
133
- cursor = index + close.length;
134
- index = string.indexOf(close, cursor);
135
- } while (~index);
136
- return result + string.substring(cursor);
137
- };
138
- var createColors = (enabled = isColorSupported) => {
139
- let f = enabled ? formatter : () => String;
140
- return {
141
- isColorSupported: enabled,
142
- reset: f("\x1B[0m", "\x1B[0m"),
143
- bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
144
- dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
145
- italic: f("\x1B[3m", "\x1B[23m"),
146
- underline: f("\x1B[4m", "\x1B[24m"),
147
- inverse: f("\x1B[7m", "\x1B[27m"),
148
- hidden: f("\x1B[8m", "\x1B[28m"),
149
- strikethrough: f("\x1B[9m", "\x1B[29m"),
150
- black: f("\x1B[30m", "\x1B[39m"),
151
- red: f("\x1B[31m", "\x1B[39m"),
152
- green: f("\x1B[32m", "\x1B[39m"),
153
- yellow: f("\x1B[33m", "\x1B[39m"),
154
- blue: f("\x1B[34m", "\x1B[39m"),
155
- magenta: f("\x1B[35m", "\x1B[39m"),
156
- cyan: f("\x1B[36m", "\x1B[39m"),
157
- white: f("\x1B[37m", "\x1B[39m"),
158
- gray: f("\x1B[90m", "\x1B[39m"),
159
- bgBlack: f("\x1B[40m", "\x1B[49m"),
160
- bgRed: f("\x1B[41m", "\x1B[49m"),
161
- bgGreen: f("\x1B[42m", "\x1B[49m"),
162
- bgYellow: f("\x1B[43m", "\x1B[49m"),
163
- bgBlue: f("\x1B[44m", "\x1B[49m"),
164
- bgMagenta: f("\x1B[45m", "\x1B[49m"),
165
- bgCyan: f("\x1B[46m", "\x1B[49m"),
166
- bgWhite: f("\x1B[47m", "\x1B[49m"),
167
- blackBright: f("\x1B[90m", "\x1B[39m"),
168
- redBright: f("\x1B[91m", "\x1B[39m"),
169
- greenBright: f("\x1B[92m", "\x1B[39m"),
170
- yellowBright: f("\x1B[93m", "\x1B[39m"),
171
- blueBright: f("\x1B[94m", "\x1B[39m"),
172
- magentaBright: f("\x1B[95m", "\x1B[39m"),
173
- cyanBright: f("\x1B[96m", "\x1B[39m"),
174
- whiteBright: f("\x1B[97m", "\x1B[39m"),
175
- bgBlackBright: f("\x1B[100m", "\x1B[49m"),
176
- bgRedBright: f("\x1B[101m", "\x1B[49m"),
177
- bgGreenBright: f("\x1B[102m", "\x1B[49m"),
178
- bgYellowBright: f("\x1B[103m", "\x1B[49m"),
179
- bgBlueBright: f("\x1B[104m", "\x1B[49m"),
180
- bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
181
- bgCyanBright: f("\x1B[106m", "\x1B[49m"),
182
- bgWhiteBright: f("\x1B[107m", "\x1B[49m")
183
- };
184
- };
185
- module.exports = createColors();
186
- module.exports.createColors = createColors;
187
- }
188
- });
189
-
190
- // src/build.ts
191
- import path3 from "path";
192
- import { build } from "vite";
193
- import { nodePolyfills } from "vite-plugin-node-polyfills";
194
-
195
- // src/SSRServer.ts
196
- var import_fastify_plugin2 = __toESM(require_plugin(), 1);
197
- var import_picocolors = __toESM(require_picocolors(), 1);
198
- import { readFile } from "fs/promises";
199
- import path2 from "path";
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
-
221
- // src/constants.ts
222
- var RENDERTYPE = {
223
- ssr: "ssr",
224
- streaming: "streaming"
225
- };
226
- var SSRTAG = {
227
- ssrHead: "<!--ssr-head-->",
228
- ssrHtml: "<!--ssr-html-->"
229
- };
230
- var TEMPLATE = {
231
- defaultEntryClient: "entry-client",
232
- defaultEntryServer: "entry-server",
233
- defaultHtmlTemplate: "index.html"
234
- };
235
- var DEV_CSP_DIRECTIVES = {
236
- "default-src": ["'self'"],
237
- "connect-src": ["'self'", "ws:", "http:"],
238
- "style-src": ["'self'", "'unsafe-inline'"],
239
- "img-src": ["'self'", "data:"]
240
- };
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
-
268
- // src/security/csp.ts
269
- var import_fastify_plugin = __toESM(require_plugin(), 1);
270
- import crypto from "crypto";
271
-
272
- // src/utils/Utils.ts
273
- import { dirname, join } from "path";
274
- import "path";
275
- import { fileURLToPath } from "url";
276
- import { match } from "path-to-regexp";
277
- var isDevelopment = process.env.NODE_ENV === "development";
278
- var __filename = fileURLToPath(import.meta.url);
279
- var __dirname = join(dirname(__filename), !isDevelopment ? "./" : "..");
280
- var CSS_LANGS_RE = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
281
- async function collectStyle(server, entries) {
282
- const urls = await collectStyleUrls(server, entries);
283
- const codes = await Promise.all(
284
- urls.map(async (url) => {
285
- const res = await server.transformRequest(url + "?direct");
286
- return [`/* [collectStyle] ${url} */`, res?.code];
287
- })
288
- );
289
- return codes.flat().filter(Boolean).join("\n\n");
290
- }
291
- async function collectStyleUrls(server, entries) {
292
- const visited = /* @__PURE__ */ new Set();
293
- async function traverse(url) {
294
- const [, id] = await server.moduleGraph.resolveUrl(url);
295
- if (visited.has(id)) return;
296
- visited.add(id);
297
- const mod = server.moduleGraph.getModuleById(id);
298
- if (!mod) return;
299
- await Promise.all([...mod.importedModules].map((childMod) => traverse(childMod.url)));
300
- }
301
- await Promise.all(entries.map((e) => server.transformRequest(e)));
302
- await Promise.all(entries.map((url) => traverse(url)));
303
- return [...visited].filter((url) => url.match(CSS_LANGS_RE));
304
- }
305
- function renderPreloadLinks(ssrManifest, basePath = "") {
306
- const seen = /* @__PURE__ */ new Set();
307
- let links = "";
308
- for (const moduleId in ssrManifest) {
309
- const files = ssrManifest[moduleId];
310
- if (files) {
311
- files.forEach((file) => {
312
- if (!seen.has(file)) {
313
- seen.add(file);
314
- links += renderPreloadLink(basePath ? `${basePath}/${file}` : `${file}`);
315
- }
316
- });
317
- }
318
- }
319
- return links;
320
- }
321
- function renderPreloadLink(file) {
322
- const fileType = file.match(/\.(js|css|woff2?|gif|jpe?g|png|svg)$/)?.[1];
323
- switch (fileType) {
324
- case "js":
325
- return `<link rel="modulepreload" href="${file}">`;
326
- case "css":
327
- return `<link rel="stylesheet" href="${file}">`;
328
- case "woff":
329
- case "woff2":
330
- return `<link rel="preload" href="${file}" as="font" type="font/${fileType}" crossorigin>`;
331
- case "gif":
332
- case "jpeg":
333
- case "jpg":
334
- case "png":
335
- return `<link rel="preload" href="${file}" as="image" type="image/${fileType}">`;
336
- case "svg":
337
- return `<link rel="preload" href="${file}" as="image" type="image/svg+xml">`;
338
- default:
339
- return "";
340
- }
341
- }
342
- var callServiceMethod = async (registry, serviceName, methodName, params) => {
343
- const service = registry[serviceName];
344
- if (!service) throw new Error(`Service ${String(serviceName)} does not exist in the registry`);
345
- const method = service[methodName];
346
- if (typeof method !== "function") throw new Error(`Service method ${String(methodName)} does not exist on ${String(serviceName)}`);
347
- const data = await method(params);
348
- if (typeof data !== "object" || data === null)
349
- throw new Error(`Expected object response from ${String(serviceName)}.${String(methodName)}, but got ${typeof data}`);
350
- return data;
351
- };
352
- var isServiceDescriptor = (obj) => {
353
- if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
354
- const maybe = obj;
355
- return typeof maybe.serviceName === "string" && typeof maybe.serviceMethod === "string";
356
- };
357
- var fetchInitialData = async (attr, params, serviceRegistry, ctx = { headers: {} }, callServiceMethodImpl = callServiceMethod) => {
358
- const dataHandler = attr?.data;
359
- if (!dataHandler || typeof dataHandler !== "function") return Promise.resolve({});
360
- return dataHandler(params, ctx).then(async (result) => {
361
- if (isServiceDescriptor(result)) {
362
- const { serviceName, serviceMethod, args } = result;
363
- if (serviceRegistry[serviceName]?.[serviceMethod]) return callServiceMethodImpl(serviceRegistry, serviceName, serviceMethod, args ?? {});
364
- throw new Error(`Invalid service: serviceName=${String(serviceName)}, method=${String(serviceMethod)}`);
365
- }
366
- if (typeof result === "object" && result !== null) return result;
367
- throw new Error("Invalid result from attr.data");
368
- });
369
- };
370
- var matchRoute = (url, renderRoutes) => {
371
- for (const route of renderRoutes) {
372
- const matcher = match(route.path, {
373
- decode: decodeURIComponent
374
- });
375
- const matched = matcher(url);
376
- if (matched) return { route, params: matched.params };
377
- }
378
- return null;
379
- };
380
- function getCssLinks(manifest, basePath = "") {
381
- const seen = /* @__PURE__ */ new Set();
382
- const styles = [];
383
- for (const key in manifest) {
384
- const entry = manifest[key];
385
- if (entry && entry.css) {
386
- for (const cssFile of entry.css) {
387
- if (!seen.has(cssFile)) {
388
- seen.add(cssFile);
389
- styles.push(`<link rel="preload stylesheet" as="style" type="text/css" href="${basePath}/${cssFile}">`);
390
- }
391
- }
392
- }
393
- }
394
- return styles.join("\n");
395
- }
396
- var overrideCSSHMRConsoleError = () => {
397
- const originalConsoleError = console.error;
398
- console.error = function(message, ...optionalParams) {
399
- if (typeof message === "string" && message.includes("css hmr is not supported in runtime mode")) return;
400
- originalConsoleError.apply(console, [message, ...optionalParams]);
401
- };
402
- };
403
- var ensureNonNull = (value, errorMessage) => {
404
- if (value === void 0 || value === null) throw new Error(errorMessage);
405
- return value;
406
- };
407
-
408
- // src/security/csp.ts
409
- var defaultGenerateCSP = (directives, nonce) => {
410
- const merged = { ...directives };
411
- merged["script-src"] = merged["script-src"] || ["'self'"];
412
- if (!merged["script-src"].some((v) => v.startsWith("'nonce-"))) merged["script-src"].push(`'nonce-${nonce}'`);
413
- if (isDevelopment) {
414
- const connect = merged["connect-src"] || ["'self'"];
415
- if (!connect.includes("ws:")) connect.push("ws:");
416
- if (!connect.includes("http:")) connect.push("http:");
417
- merged["connect-src"] = connect;
418
- const style = merged["style-src"] || ["'self'"];
419
- if (!style.includes("'unsafe-inline'")) style.push("'unsafe-inline'");
420
- merged["style-src"] = style;
421
- }
422
- return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
423
- };
424
- var generateNonce = () => crypto.randomBytes(16).toString("base64");
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"
439
- }
440
- );
441
-
442
- // src/security/verifyMiddleware.ts
443
- var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
444
- var hasAuthenticate = (app) => typeof app.authenticate === "function";
445
- var verifyContracts = (app, routes, contracts, isDebug) => {
446
- const logger = createLogger(Boolean(isDebug));
447
- for (const contract of contracts) {
448
- const isUsed = routes.some(contract.required);
449
- if (!isUsed) {
450
- debugLog(logger, `Middleware "${contract.key}" not used in any routes`);
451
- continue;
452
- }
453
- if (!contract.verify(app)) {
454
- const error = new Error(`[\u03C4js] ${contract.errorMessage}`);
455
- logger.error(error.message);
456
- throw error;
457
- }
458
- debugLog(logger, `Middleware "${contract.key}" verified \u2713`);
459
- }
460
- };
461
-
462
- // src/SSRServer.ts
463
- var createMaps = () => {
464
- return {
465
- bootstrapModules: /* @__PURE__ */ new Map(),
466
- cssLinks: /* @__PURE__ */ new Map(),
467
- manifests: /* @__PURE__ */ new Map(),
468
- preloadLinks: /* @__PURE__ */ new Map(),
469
- renderModules: /* @__PURE__ */ new Map(),
470
- ssrManifests: /* @__PURE__ */ new Map(),
471
- templates: /* @__PURE__ */ new Map()
472
- };
473
- };
474
- var processConfigs = (configs, baseClientRoot, templateDefaults) => {
475
- return configs.map((config) => {
476
- const clientRoot = path2.resolve(baseClientRoot, config.entryPoint);
477
- return {
478
- clientRoot,
479
- entryPoint: config.entryPoint,
480
- entryClient: config.entryClient || templateDefaults.defaultEntryClient,
481
- entryServer: config.entryServer || templateDefaults.defaultEntryServer,
482
- htmlTemplate: config.htmlTemplate || templateDefaults.defaultHtmlTemplate,
483
- appId: config.appId
484
- };
485
- });
486
- };
487
- var SSRServer = (0, import_fastify_plugin2.default)(
488
- async (app, opts) => {
489
- const logger = createLogger(opts.isDebug ?? false);
490
- const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
491
- const { bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates } = createMaps();
492
- const processedConfigs = processConfigs(configs, baseClientRoot, TEMPLATE);
493
- for (const config of processedConfigs) {
494
- const { clientRoot, entryClient, htmlTemplate } = config;
495
- const templateHtmlPath = path2.join(clientRoot, htmlTemplate);
496
- const templateHtml = await readFile(templateHtmlPath, "utf-8");
497
- templates.set(clientRoot, templateHtml);
498
- const relativeBasePath = path2.relative(baseClientRoot, clientRoot).replace(/\\/g, "/");
499
- const adjustedRelativePath = relativeBasePath ? `/${relativeBasePath}` : "";
500
- if (!isDevelopment) {
501
- const manifestPath = path2.join(clientRoot, ".vite/manifest.json");
502
- const manifestContent = await readFile(manifestPath, "utf-8");
503
- const manifest = JSON.parse(manifestContent);
504
- manifests.set(clientRoot, manifest);
505
- const ssrManifestPath = path2.join(clientRoot, ".vite/ssr-manifest.json");
506
- const ssrManifestContent = await readFile(ssrManifestPath, "utf-8");
507
- const ssrManifest = JSON.parse(ssrManifestContent);
508
- ssrManifests.set(clientRoot, ssrManifest);
509
- const entryClientFile = manifest[`${entryClient}.tsx`]?.file;
510
- if (!entryClientFile) throw new Error(`Entry client file not found in manifest for ${entryClient}.tsx`);
511
- const bootstrapModule = `/${adjustedRelativePath}/${entryClientFile}`.replace(/\/{2,}/g, "/");
512
- bootstrapModules.set(clientRoot, bootstrapModule);
513
- const preloadLink = renderPreloadLinks(ssrManifest, adjustedRelativePath);
514
- preloadLinks.set(clientRoot, preloadLink);
515
- const cssLink = getCssLinks(manifest, adjustedRelativePath);
516
- cssLinks.set(clientRoot, cssLink);
517
- } else {
518
- const bootstrapModule = `/${adjustedRelativePath}/${entryClient}`.replace(/\/{2,}/g, "/");
519
- bootstrapModules.set(clientRoot, bootstrapModule);
520
- }
521
- }
522
- let viteDevServer;
523
- verifyContracts(
524
- app,
525
- routes,
526
- [
527
- {
528
- key: "auth",
529
- required: isAuthRequired,
530
- verify: hasAuthenticate,
531
- errorMessage: "Routes require auth but Fastify instance is missing `.authenticate` decorator."
532
- }
533
- ],
534
- opts.isDebug
535
- );
536
- if (opts.registerStaticAssets && typeof opts.registerStaticAssets === "object") {
537
- const { plugin, options } = opts.registerStaticAssets;
538
- await app.register(plugin, {
539
- root: baseClientRoot,
540
- prefix: "/",
541
- index: false,
542
- wildcard: false,
543
- ...options ?? {}
544
- });
545
- }
546
- app.register(cspPlugin, {
547
- directives: opts.security?.csp?.directives,
548
- generateCSP: opts.security?.csp?.generateCSP
549
- });
550
- app.addHook("onRequest", createAuthHook(routes));
551
- if (isDevelopment) {
552
- const { createServer } = await import("vite");
553
- viteDevServer = await createServer({
554
- appType: "custom",
555
- css: {
556
- preprocessorOptions: {
557
- scss: {
558
- api: "modern-compiler"
559
- }
560
- }
561
- },
562
- mode: "development",
563
- plugins: [
564
- ...isDebug ? [
565
- {
566
- name: "taujs-development-server-debug-logging",
567
- configureServer(server) {
568
- logger.log(import_picocolors.default.green("\u03C4js development server debug started."));
569
- server.middlewares.use((req, res, next) => {
570
- logger.log(import_picocolors.default.cyan(`\u2190 rx: ${req.url}`));
571
- res.on("finish", () => logger.log(import_picocolors.default.yellow(`\u2192 tx: ${req.url}`)));
572
- next();
573
- });
574
- }
575
- }
576
- ] : []
577
- ],
578
- resolve: {
579
- alias: {
580
- "@client": path2.resolve(baseClientRoot),
581
- "@server": path2.resolve(__dirname),
582
- "@shared": path2.resolve(__dirname, "../shared"),
583
- ...alias
584
- }
585
- },
586
- root: baseClientRoot,
587
- server: {
588
- middlewareMode: true,
589
- hmr: {
590
- port: 5174
591
- }
592
- }
593
- });
594
- overrideCSSHMRConsoleError();
595
- app.addHook("onRequest", async (request, reply) => {
596
- await new Promise((resolve) => {
597
- viteDevServer.middlewares(request.raw, reply.raw, () => {
598
- if (!reply.sent) resolve();
599
- });
600
- });
601
- });
602
- }
603
- app.get("/*", async (req, reply) => {
604
- try {
605
- if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
606
- const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
607
- const matchedRoute = matchRoute(url, routes);
608
- const cspNonce = req.cspNonce;
609
- if (!matchedRoute) {
610
- reply.callNotFound();
611
- return;
612
- }
613
- const { route, params } = matchedRoute;
614
- const { attr, appId } = route;
615
- const config = processedConfigs.find((config2) => config2.appId === appId) || processedConfigs[0];
616
- if (!config) throw new Error("No configuration found for the request.");
617
- const { clientRoot, entryServer } = config;
618
- let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
619
- const bootstrapModule = bootstrapModules.get(clientRoot);
620
- const cssLink = cssLinks.get(clientRoot);
621
- const manifest = manifests.get(clientRoot);
622
- const preloadLink = preloadLinks.get(clientRoot);
623
- const ssrManifest = ssrManifests.get(clientRoot);
624
- let renderModule;
625
- if (isDevelopment) {
626
- template = template.replace(/<script type="module" src="\/@vite\/client"><\/script>/g, "");
627
- template = template.replace(/<style type="text\/css">[\s\S]*?<\/style>/g, "");
628
- const entryServerPath = path2.join(clientRoot, `${entryServer}.tsx`);
629
- const executedModule = await viteDevServer.ssrLoadModule(entryServerPath);
630
- renderModule = executedModule;
631
- const styles = await collectStyle(viteDevServer, [entryServerPath]);
632
- template = template?.replace("</head>", `<style type="text/css">${styles}</style></head>`);
633
- template = await viteDevServer.transformIndexHtml(url, template);
634
- } else {
635
- renderModule = renderModules.get(clientRoot);
636
- if (!renderModule) {
637
- const renderModulePath = path2.join(clientRoot, `${entryServer}.js`);
638
- const importedModule = await import(renderModulePath);
639
- renderModule = importedModule;
640
- renderModules.set(clientRoot, renderModule);
641
- }
642
- }
643
- const renderType = attr?.render || RENDERTYPE.ssr;
644
- const [beforeBody = "", afterBody = ""] = template.split(SSRTAG.ssrHtml);
645
- const [beforeHead = "", afterHead = ""] = beforeBody.split(SSRTAG.ssrHead);
646
- const initialDataPromise = fetchInitialData(attr, params, serviceRegistry);
647
- if (renderType === RENDERTYPE.ssr) {
648
- const { renderSSR } = renderModule;
649
- const initialDataResolved = await initialDataPromise;
650
- const initialDataScript = `<script nonce="${cspNonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
651
- const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
652
- let aggregateHeadContent = headContent;
653
- if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
654
- if (manifest && cssLink) aggregateHeadContent += cssLink;
655
- const shouldHydrate = attr?.hydrate !== false;
656
- const bootstrapScriptTag = shouldHydrate ? `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
657
- const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
658
- return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
659
- } else {
660
- const { renderStream } = renderModule;
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
- });
667
- renderStream(
668
- reply.raw,
669
- {
670
- onHead: (headContent) => {
671
- let aggregateHeadContent = headContent;
672
- if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
673
- if (manifest && cssLink) aggregateHeadContent += cssLink;
674
- reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
675
- },
676
- onFinish: async (initialDataResolved) => {
677
- reply.raw.write(
678
- `<script nonce="${cspNonce2}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`
679
- );
680
- reply.raw.write(afterBody);
681
- reply.raw.end();
682
- },
683
- onError: (error) => {
684
- console.error("Critical rendering onError:", error);
685
- reply.raw.end("Internal Server Error");
686
- }
687
- },
688
- initialDataPromise,
689
- req.url,
690
- bootstrapModule,
691
- attr?.meta,
692
- cspNonce2
693
- );
694
- }
695
- } catch (error) {
696
- console.error("Error setting up SSR stream:", error);
697
- if (!reply.raw.headersSent) reply.raw.writeHead(500, { "Content-Type": "text/plain" });
698
- reply.raw.end("Internal Server Error");
699
- }
700
- });
701
- app.setNotFoundHandler(async (req, reply) => {
702
- if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
703
- try {
704
- const defaultConfig = processedConfigs[0];
705
- if (!defaultConfig) throw new Error("No default configuration found.");
706
- const { clientRoot } = defaultConfig;
707
- const cspNonce = req.cspNonce;
708
- let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
709
- const cssLink = cssLinks.get(clientRoot);
710
- const bootstrapModule = bootstrapModules.get(clientRoot);
711
- template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
712
- if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
713
- if (bootstrapModule)
714
- template = template.replace("</body>", `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
715
- reply.status(200).type("text/html").send(template);
716
- } catch (error) {
717
- console.error("Failed to serve clientHtmlTemplate:", error);
718
- reply.status(500).send("Internal Server Error");
719
- }
720
- });
721
- },
722
- { name: "taujs-ssr-server" }
723
- );
724
-
725
- // src/build.ts
726
- async function taujsBuild({
727
- configs,
728
- projectRoot,
729
- clientBaseDir,
730
- isSSRBuild = process.env.BUILD_MODE === "ssr"
731
- }) {
732
- const deleteDist = async () => {
733
- const { rm } = await import("fs/promises");
734
- const distPath = path3.resolve(projectRoot, "dist");
735
- try {
736
- await rm(distPath, { recursive: true, force: true });
737
- console.log("Deleted the dist directory\n");
738
- } catch (err) {
739
- console.error("Error deleting dist directory:", err);
740
- }
741
- };
742
- const processedConfigs = processConfigs(configs, clientBaseDir, TEMPLATE);
743
- if (!isSSRBuild) await deleteDist();
744
- for (const config of processedConfigs) {
745
- const { appId, entryPoint, clientRoot, entryClient, entryServer, htmlTemplate, plugins = [] } = config;
746
- const outDir = path3.resolve(projectRoot, `dist/client/${entryPoint}`);
747
- const root = entryPoint ? path3.resolve(clientBaseDir, entryPoint) : clientBaseDir;
748
- const server = path3.resolve(clientRoot, `${entryServer}.tsx`);
749
- const client = path3.resolve(clientRoot, `${entryClient}.tsx`);
750
- const main = path3.resolve(clientRoot, htmlTemplate);
751
- const viteConfig = {
752
- base: entryPoint ? `/${entryPoint}/` : "/",
753
- build: {
754
- outDir,
755
- manifest: !isSSRBuild,
756
- rollupOptions: {
757
- input: isSSRBuild ? { server } : { client, main }
758
- },
759
- ssr: isSSRBuild ? server : void 0,
760
- ssrManifest: isSSRBuild,
761
- ...isSSRBuild && {
762
- format: "esm",
763
- target: `node${process.versions.node.split(".").map(Number)[0]}`
764
- }
765
- },
766
- css: {
767
- preprocessorOptions: {
768
- scss: { api: "modern-compiler" }
769
- }
770
- },
771
- plugins: [...config.plugins ?? [], nodePolyfills({ include: ["fs", "stream"] })],
772
- publicDir: "public",
773
- resolve: {
774
- alias: {
775
- "@client": root,
776
- "@server": path3.resolve(projectRoot, "src/server"),
777
- "@shared": path3.resolve(projectRoot, "src/shared")
778
- }
779
- },
780
- root,
781
- server: {
782
- proxy: {
783
- "/api": {
784
- target: "http://localhost:3000",
785
- changeOrigin: true,
786
- rewrite: (path4) => path4.replace(/^\/api/, "")
787
- }
788
- }
789
- }
790
- };
791
- try {
792
- console.log(`Building for entryPoint: "${entryPoint}" (${appId})`);
793
- await build(viteConfig);
794
- console.log(`Build complete for entryPoint: "${entryPoint}"
795
- `);
796
- } catch (error) {
797
- console.error(`Error building for entryPoint: "${entryPoint}"
798
- `, error);
799
- process.exit(1);
800
- }
801
- }
802
- }
803
- export {
804
- taujsBuild
805
- };