@taujs/server 0.5.3 → 0.5.4

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/index.js CHANGED
@@ -99,62 +99,19 @@ var require_picocolors = __commonJS({
99
99
 
100
100
  // src/CreateServer.ts
101
101
  var import_picocolors4 = __toESM(require_picocolors(), 1);
102
- import path5 from "path";
103
- import { performance as performance3 } from "perf_hooks";
104
- import Fastify from "fastify";
105
-
106
- // src/Setup.ts
102
+ import path6 from "path";
107
103
  import { performance } from "perf_hooks";
104
+ import Fastify from "fastify";
108
105
 
109
- // src/constants.ts
110
- var import_picocolors = __toESM(require_picocolors(), 1);
111
- var RENDERTYPE = {
112
- ssr: "ssr",
113
- streaming: "streaming"
114
- };
115
- var SSRTAG = {
116
- ssrHead: "<!--ssr-head-->",
117
- ssrHtml: "<!--ssr-html-->"
118
- };
119
- var TEMPLATE = {
120
- defaultEntryClient: "entry-client",
121
- defaultEntryServer: "entry-server",
122
- defaultHtmlTemplate: "index.html"
123
- };
124
- var DEV_CSP_DIRECTIVES = {
125
- "default-src": ["'self'"],
126
- "connect-src": ["'self'", "ws:", "http:"],
127
- "style-src": ["'self'", "'unsafe-inline'"],
128
- "img-src": ["'self'", "data:"]
129
- };
130
- var CONTENT = {
131
- TAG: "\u03C4js"
132
- };
133
- var DEBUG = {
134
- auth: { label: "auth", colour: import_picocolors.default.blue },
135
- csp: { label: "csp", colour: import_picocolors.default.yellow },
136
- errors: { label: "errors", colour: import_picocolors.default.red },
137
- routes: { label: "routes", colour: import_picocolors.default.cyan },
138
- security: { label: "security", colour: import_picocolors.default.yellow },
139
- trx: { label: "trx", colour: import_picocolors.default.magenta },
140
- vite: { label: "vite", colour: import_picocolors.default.yellow }
141
- };
142
- var REGEX = {
143
- BENIGN_NET_ERR: /\b(?:ECONNRESET|EPIPE|ECONNABORTED)\b|socket hang up|aborted|premature(?: close)?/i,
144
- SAFE_TRACE: /^[a-zA-Z0-9-_:.]{1,128}$/
145
- };
146
- var ENTRY_EXTENSIONS = [".ts", ".tsx"];
106
+ // src/core/telemetry/Telemetry.ts
107
+ var now = () => globalThis.performance?.now?.() ?? Date.now();
147
108
 
148
- // src/Setup.ts
109
+ // src/core/config/Setup.ts
149
110
  var extractBuildConfigs = (config) => {
150
- return config.apps.map(({ appId, entryPoint, plugins }) => ({
151
- appId,
152
- entryPoint,
153
- plugins
154
- }));
111
+ return config.apps.map(({ appId, entryPoint, plugins }) => ({ appId, entryPoint, plugins }));
155
112
  };
156
113
  var extractRoutes = (taujsConfig) => {
157
- const t0 = performance.now();
114
+ const t0 = now();
158
115
  const allRoutes = [];
159
116
  const apps = [];
160
117
  const warnings = [];
@@ -173,7 +130,7 @@ var extractRoutes = (taujsConfig) => {
173
130
  if (appIds.length > 1) warnings.push(`Route path "${path7}" is declared in multiple apps: ${appIds.join(", ")}`);
174
131
  }
175
132
  const sortedRoutes = allRoutes.sort((a, b) => computeScore(b.path) - computeScore(a.path));
176
- const durationMs = performance.now() - t0;
133
+ const durationMs = now() - t0;
177
134
  return {
178
135
  routes: sortedRoutes,
179
136
  apps,
@@ -183,7 +140,7 @@ var extractRoutes = (taujsConfig) => {
183
140
  };
184
141
  };
185
142
  var extractSecurity = (taujsConfig) => {
186
- const t0 = performance.now();
143
+ const t0 = now();
187
144
  const user = taujsConfig.security ?? {};
188
145
  const userCsp = user.csp;
189
146
  const hasExplicitCSP = !!userCsp;
@@ -204,7 +161,7 @@ var extractSecurity = (taujsConfig) => {
204
161
  hasReporting: !!normalisedCsp?.reporting?.endpoint,
205
162
  reportOnly: !!normalisedCsp?.reporting?.reportOnly
206
163
  };
207
- const durationMs = performance.now() - t0;
164
+ const durationMs = now() - t0;
208
165
  return {
209
166
  security,
210
167
  durationMs,
@@ -212,58 +169,11 @@ var extractSecurity = (taujsConfig) => {
212
169
  summary
213
170
  };
214
171
  };
215
- function printConfigSummary(logger, apps, configsCount, totalRoutes, durationMs, warnings) {
216
- logger.info({}, `${CONTENT.TAG} [config] Loaded ${configsCount} app(s), ${totalRoutes} route(s) in ${durationMs.toFixed(1)}ms`);
217
- apps.forEach((a) => logger.debug("routes", {}, `\u2022 ${a.appId}: ${a.routeCount} route(s)`));
218
- warnings.forEach((w) => logger.warn({}, `${CONTENT.TAG} [warn] ${w}`));
219
- }
220
- function printSecuritySummary(logger, routes, security, hasExplicitCSP, securityDurationMs) {
221
- const total = routes.length;
222
- const disabled = routes.filter((r) => r.attr?.middleware?.csp === false).length;
223
- const custom = routes.filter((r) => {
224
- const v = r.attr?.middleware?.csp;
225
- return v !== void 0 && v !== false;
226
- }).length;
227
- const enabled = total - disabled;
228
- const hasReporting = !!security.csp?.reporting?.endpoint;
229
- const mode = security.csp?.defaultMode ?? "merge";
230
- let status = "configured";
231
- let detail = "";
232
- if (hasExplicitCSP) {
233
- detail = `explicit, mode=${mode}`;
234
- if (hasReporting) detail += ", reporting";
235
- if (custom > 0) detail += `, ${custom} route override(s)`;
236
- } else {
237
- if (process.env.NODE_ENV === "production") {
238
- logger.warn({}, "(consider explicit config for production)");
239
- }
240
- }
241
- logger.info({}, `${CONTENT.TAG} [security] CSP ${status} (${enabled}/${total} routes) in ${securityDurationMs.toFixed(1)}ms`);
242
- }
243
- function printContractReport(logger, report) {
244
- for (const r of report.items) {
245
- const line = `${CONTENT.TAG} [security][${r.key}] ${r.message}`;
246
- if (r.status === "error") {
247
- logger.error({}, line);
248
- } else if (r.status === "warning") {
249
- logger.warn({}, line);
250
- } else if (r.status === "skipped") {
251
- logger.debug(r.key, {}, line);
252
- } else {
253
- logger.info({}, line);
254
- }
255
- }
256
- }
257
172
  var computeScore = (path7) => {
258
173
  return path7.split("/").filter(Boolean).reduce((score, segment) => score + (segment.startsWith(":") ? 1 : 10), 0);
259
174
  };
260
- function printVitePluginSummary(logger, appPlugins, merged) {
261
- const mergedNames = merged.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
262
- const appsLine = appPlugins.length === 0 ? "no app plugins" : appPlugins.map((a) => `${a.appId}=[${a.plugins.join(", ") || "none"}]`).join(" ");
263
- logger.info(void 0, `${CONTENT.TAG} [vite] Plugins ${appsLine} merged=[${mergedNames.join(", ") || "none"}]`);
264
- }
265
175
 
266
- // src/logging/AppError.ts
176
+ // src/core/errors/AppError.ts
267
177
  var HTTP_STATUS = {
268
178
  infra: 500,
269
179
  upstream: 502,
@@ -387,6 +297,40 @@ function toReason(e) {
387
297
  return new Error(String(e));
388
298
  }
389
299
 
300
+ // src/constants.ts
301
+ var import_picocolors = __toESM(require_picocolors(), 1);
302
+ var SSRTAG = {
303
+ ssrHead: "<!--ssr-head-->",
304
+ ssrHtml: "<!--ssr-html-->"
305
+ };
306
+ var TEMPLATE = {
307
+ defaultEntryClient: "entry-client",
308
+ defaultEntryServer: "entry-server",
309
+ defaultHtmlTemplate: "index.html"
310
+ };
311
+ var ENTRY_EXTENSIONS = [".ts", ".tsx"];
312
+ var DEV_CSP_DIRECTIVES = {
313
+ "default-src": ["'self'"],
314
+ "connect-src": ["'self'", "ws:", "http:"],
315
+ "style-src": ["'self'", "'unsafe-inline'"],
316
+ "img-src": ["'self'", "data:"]
317
+ };
318
+ var CONTENT = {
319
+ TAG: "\u03C4js"
320
+ };
321
+ var DEBUG = {
322
+ auth: { label: "auth", colour: import_picocolors.default.blue },
323
+ csp: { label: "csp", colour: import_picocolors.default.yellow },
324
+ errors: { label: "errors", colour: import_picocolors.default.red },
325
+ routes: { label: "routes", colour: import_picocolors.default.cyan },
326
+ security: { label: "security", colour: import_picocolors.default.yellow },
327
+ trx: { label: "trx", colour: import_picocolors.default.magenta },
328
+ vite: { label: "vite", colour: import_picocolors.default.yellow }
329
+ };
330
+ var REGEX = {
331
+ BENIGN_NET_ERR: /\b(?:ECONNRESET|EPIPE|ECONNABORTED)\b|socket hang up|aborted|premature(?: close)?/i
332
+ };
333
+
390
334
  // src/logging/Logger.ts
391
335
  var import_picocolors2 = __toESM(require_picocolors(), 1);
392
336
 
@@ -451,8 +395,10 @@ function parseDebugInput(input) {
451
395
  return input;
452
396
  }
453
397
 
454
- // src/logging/Logger.ts
398
+ // src/core/logging/types.ts
455
399
  var DEBUG_CATEGORIES = ["auth", "routes", "errors", "vite", "network", "ssr"];
400
+
401
+ // src/logging/Logger.ts
456
402
  var Logger = class _Logger {
457
403
  constructor(config = {}) {
458
404
  this.config = config;
@@ -512,12 +458,12 @@ var Logger = class _Logger {
512
458
  return copy;
513
459
  }
514
460
  formatTimestamp() {
515
- const now = /* @__PURE__ */ new Date();
516
- if (process.env.NODE_ENV === "production") return now.toISOString();
517
- const hours = String(now.getHours()).padStart(2, "0");
518
- const minutes = String(now.getMinutes()).padStart(2, "0");
519
- const seconds = String(now.getSeconds()).padStart(2, "0");
520
- const millis = String(now.getMilliseconds()).padStart(3, "0");
461
+ const now2 = /* @__PURE__ */ new Date();
462
+ if (process.env.NODE_ENV === "production") return now2.toISOString();
463
+ const hours = String(now2.getHours()).padStart(2, "0");
464
+ const minutes = String(now2.getMinutes()).padStart(2, "0");
465
+ const seconds = String(now2.getSeconds()).padStart(2, "0");
466
+ const millis = String(now2.getMilliseconds()).padStart(3, "0");
521
467
  return `${hours}:${minutes}:${seconds}.${millis}`;
522
468
  }
523
469
  emit(level, message, meta, category) {
@@ -765,48 +711,79 @@ var verifyContracts = (app, routes, contracts, security) => {
765
711
  return { items };
766
712
  };
767
713
 
714
+ // src/Setup.ts
715
+ function printConfigSummary(logger, apps, configsCount, totalRoutes, durationMs, warnings) {
716
+ logger.info({}, `${CONTENT.TAG} [config] Loaded ${configsCount} app(s), ${totalRoutes} route(s) in ${durationMs.toFixed(1)}ms`);
717
+ apps.forEach((a) => logger.debug("routes", {}, `\u2022 ${a.appId}: ${a.routeCount} route(s)`));
718
+ warnings.forEach((w) => logger.warn({}, `${CONTENT.TAG} [warn] ${w}`));
719
+ }
720
+ function printSecuritySummary(logger, routes, security, hasExplicitCSP, securityDurationMs) {
721
+ const total = routes.length;
722
+ const disabled = routes.filter((r) => r.attr?.middleware?.csp === false).length;
723
+ const custom = routes.filter((r) => {
724
+ const v = r.attr?.middleware?.csp;
725
+ return v !== void 0 && v !== false;
726
+ }).length;
727
+ const enabled = total - disabled;
728
+ const hasReporting = !!security.csp?.reporting?.endpoint;
729
+ const mode = security.csp?.defaultMode ?? "merge";
730
+ let status = "configured";
731
+ let detail = "";
732
+ if (hasExplicitCSP) {
733
+ detail = `explicit, mode=${mode}`;
734
+ if (hasReporting) detail += ", reporting";
735
+ if (custom > 0) detail += `, ${custom} route override(s)`;
736
+ } else {
737
+ if (process.env.NODE_ENV === "production") {
738
+ logger.warn({}, "(consider explicit config for production)");
739
+ }
740
+ }
741
+ logger.info({}, `${CONTENT.TAG} [security] CSP ${status} (${enabled}/${total} routes) in ${securityDurationMs.toFixed(1)}ms`);
742
+ }
743
+ function printContractReport(logger, report) {
744
+ for (const r of report.items) {
745
+ const line = `${CONTENT.TAG} [security][${r.key}] ${r.message}`;
746
+ if (r.status === "error") {
747
+ logger.error({}, line);
748
+ } else if (r.status === "warning") {
749
+ logger.warn({}, line);
750
+ } else if (r.status === "skipped") {
751
+ logger.debug(r.key, {}, line);
752
+ } else {
753
+ logger.info({}, line);
754
+ }
755
+ }
756
+ }
757
+ function printVitePluginSummary(logger, appPlugins, merged) {
758
+ const mergedNames = merged.map((p) => p?.name).filter((n) => typeof n === "string" && n.length > 0);
759
+ const appsLine = appPlugins.length === 0 ? "no app plugins" : appPlugins.map((a) => `${a.appId}=[${a.plugins.join(", ") || "none"}]`).join(" ");
760
+ logger.info(void 0, `${CONTENT.TAG} [vite] Plugins ${appsLine} merged=[${mergedNames.join(", ") || "none"}]`);
761
+ }
762
+
768
763
  // src/SSRServer.ts
769
764
  import fp3 from "fastify-plugin";
770
765
 
771
- // src/logging/utils/index.ts
772
- var httpStatusFrom = (err, fallback = 500) => err instanceof AppError ? err.httpStatus : fallback;
773
- var toHttp = (err) => {
774
- const app = AppError.from(err);
775
- const status = httpStatusFrom(app);
776
- const errorMessage = app.safeMessage;
777
- return {
778
- status,
779
- body: {
780
- error: errorMessage,
781
- ...app.code && { code: app.code },
782
- statusText: statusText(status)
783
- }
784
- };
785
- };
786
- var statusText = (status) => {
787
- const map = {
788
- 400: "Bad Request",
789
- 401: "Unauthorized",
790
- 403: "Forbidden",
791
- 404: "Not Found",
792
- 405: "Method Not Allowed",
793
- 408: "Request Timeout",
794
- 422: "Unprocessable Entity",
795
- 429: "Too Many Requests",
796
- 499: "Client Closed Request",
797
- 500: "Internal Server Error",
798
- 502: "Bad Gateway",
799
- 503: "Service Unavailable",
800
- 504: "Gateway Timeout"
801
- };
802
- return map[status] ?? "Error";
766
+ // src/core/routes/DataRoutes.ts
767
+ import { match } from "path-to-regexp";
768
+
769
+ // src/core/logging/noop.ts
770
+ var noopLogger = {
771
+ debug: () => {
772
+ },
773
+ info: () => {
774
+ },
775
+ warn: () => {
776
+ },
777
+ error: () => {
778
+ },
779
+ child: () => noopLogger,
780
+ isDebugEnabled: () => false
803
781
  };
804
782
 
805
- // src/utils/DataRoutes.ts
806
- import { match } from "path-to-regexp";
783
+ // src/core/logging/resolve.ts
784
+ var resolveLogs = (logger) => logger ?? noopLogger;
807
785
 
808
- // src/utils/DataServices.ts
809
- import { performance as performance2 } from "perf_hooks";
786
+ // src/core/services/DataServices.ts
810
787
  function createCaller(registry, ctx) {
811
788
  return (serviceName, methodName, args) => callServiceMethod(registry, serviceName, methodName, args ?? {}, ctx);
812
789
  }
@@ -819,26 +796,27 @@ async function callServiceMethod(registry, serviceName, methodName, params, ctx)
819
796
  if (!service) throw AppError.notFound(`Unknown service: ${serviceName}`);
820
797
  const method = service[methodName];
821
798
  if (!method) throw AppError.notFound(`Unknown method: ${serviceName}.${methodName}`);
822
- const logger = ctx.logger?.child?.({
799
+ const baseLogger = resolveLogs(ctx.logger);
800
+ const logger = baseLogger.child({
823
801
  component: "service-call",
824
802
  service: serviceName,
825
803
  method: methodName,
826
804
  traceId: ctx.traceId
827
805
  });
828
- const t0 = performance2.now();
806
+ const t0 = now();
829
807
  try {
830
808
  const result = await method(params ?? {}, ctx);
831
809
  if (typeof result !== "object" || result === null) {
832
810
  throw AppError.internal(`Non-object result from ${serviceName}.${methodName}`);
833
811
  }
834
- logger?.debug?.({ ms: +(performance2.now() - t0).toFixed(1) }, "Service method ok");
812
+ logger.debug({ ms: +(now() - t0).toFixed(1) }, "Service method ok");
835
813
  return result;
836
814
  } catch (err) {
837
- logger?.error?.(
815
+ logger.error(
838
816
  {
839
817
  params,
840
818
  error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err),
841
- ms: +(performance2.now() - t0).toFixed(1)
819
+ ms: +(now() - t0).toFixed(1)
842
820
  },
843
821
  "Service method failed"
844
822
  );
@@ -855,7 +833,7 @@ var isServiceDescriptor = (obj) => {
855
833
  return true;
856
834
  };
857
835
 
858
- // src/utils/DataRoutes.ts
836
+ // src/core/routes/DataRoutes.ts
859
837
  var safeDecode = (value) => {
860
838
  try {
861
839
  return decodeURIComponent(value);
@@ -953,43 +931,86 @@ var fetchInitialData = async (attr, params, serviceRegistry, ctx, callServiceMet
953
931
  }
954
932
  };
955
933
 
956
- // src/security/Auth.ts
957
- var createAuthHook = (routeMatchers, logger) => {
958
- return async function authHook(req, reply) {
959
- const url = new URL(req.url, `http://${req.headers.host}`).pathname;
960
- const match2 = matchRoute(url, routeMatchers);
961
- if (!match2) return;
962
- const { route } = match2;
963
- const authConfig = route.attr?.middleware?.auth;
964
- req.routeMeta = {
965
- path: route.path,
966
- appId: route.appId,
967
- attr: {
968
- middleware: {
969
- auth: route.attr?.middleware?.auth
970
- },
971
- render: route.attr?.render
972
- }
973
- };
974
- if (!authConfig) {
975
- logger.debug("auth", { method: req.method, url: req.url }, "(none)");
976
- return;
977
- }
978
- if (typeof req.server.authenticate !== "function") {
979
- logger.warn(
980
- {
981
- path: url,
982
- appId: route.appId
983
- },
984
- "Route requires auth but Fastify authenticate decorator is missing"
985
- );
986
- return reply.status(500).send("Server misconfiguration: auth decorator missing.");
987
- }
988
- try {
989
- logger.debug("auth", { method: req.method, url: req.url }, "Invoking authenticate(...)");
990
- await req.server.authenticate(req, reply);
991
- logger.debug("auth", { method: req.method, url: req.url }, "Authentication successful");
992
- } catch (err) {
934
+ // src/System.ts
935
+ import { dirname, join } from "path";
936
+ import "path";
937
+ import { fileURLToPath } from "url";
938
+ var isDevelopment = process.env.NODE_ENV === "development";
939
+ var __filename = fileURLToPath(import.meta.url);
940
+ var DIR_SUFFIX = isDevelopment ? ".." : "./";
941
+ var __dirname = join(dirname(__filename), DIR_SUFFIX);
942
+
943
+ // src/logging/utils/index.ts
944
+ var httpStatusFrom = (err, fallback = 500) => err instanceof AppError ? err.httpStatus : fallback;
945
+ var toHttp = (err) => {
946
+ const app = AppError.from(err);
947
+ const status = httpStatusFrom(app);
948
+ const errorMessage = app.safeMessage;
949
+ return {
950
+ status,
951
+ body: {
952
+ error: errorMessage,
953
+ ...app.code && { code: app.code },
954
+ statusText: statusText(status)
955
+ }
956
+ };
957
+ };
958
+ var statusText = (status) => {
959
+ const map = {
960
+ 400: "Bad Request",
961
+ 401: "Unauthorized",
962
+ 403: "Forbidden",
963
+ 404: "Not Found",
964
+ 405: "Method Not Allowed",
965
+ 408: "Request Timeout",
966
+ 422: "Unprocessable Entity",
967
+ 429: "Too Many Requests",
968
+ 499: "Client Closed Request",
969
+ 500: "Internal Server Error",
970
+ 502: "Bad Gateway",
971
+ 503: "Service Unavailable",
972
+ 504: "Gateway Timeout"
973
+ };
974
+ return map[status] ?? "Error";
975
+ };
976
+
977
+ // src/security/Auth.ts
978
+ var createAuthHook = (routeMatchers, logger) => {
979
+ return async function authHook(req, reply) {
980
+ const url = new URL(req.url, `http://${req.headers.host}`).pathname;
981
+ const match2 = matchRoute(url, routeMatchers);
982
+ if (!match2) return;
983
+ const { route } = match2;
984
+ const authConfig = route.attr?.middleware?.auth;
985
+ req.routeMeta = {
986
+ path: route.path,
987
+ appId: route.appId,
988
+ attr: {
989
+ middleware: {
990
+ auth: route.attr?.middleware?.auth
991
+ },
992
+ render: route.attr?.render
993
+ }
994
+ };
995
+ if (!authConfig) {
996
+ logger.debug("auth", { method: req.method, url: req.url }, "(none)");
997
+ return;
998
+ }
999
+ if (typeof req.server.authenticate !== "function") {
1000
+ logger.warn(
1001
+ {
1002
+ path: url,
1003
+ appId: route.appId
1004
+ },
1005
+ "Route requires auth but Fastify authenticate decorator is missing"
1006
+ );
1007
+ return reply.status(500).send("Server misconfiguration: auth decorator missing.");
1008
+ }
1009
+ try {
1010
+ logger.debug("auth", { method: req.method, url: req.url }, "Invoking authenticate(...)");
1011
+ await req.server.authenticate(req, reply);
1012
+ logger.debug("auth", { method: req.method, url: req.url }, "Authentication successful");
1013
+ } catch (err) {
993
1014
  logger.debug("auth", { method: req.method, url: req.url }, "Authentication failed");
994
1015
  return reply.send(err);
995
1016
  }
@@ -999,16 +1020,6 @@ var createAuthHook = (routeMatchers, logger) => {
999
1020
  // src/security/CSP.ts
1000
1021
  import fp from "fastify-plugin";
1001
1022
  import crypto from "crypto";
1002
-
1003
- // src/utils/System.ts
1004
- import { dirname, join } from "path";
1005
- import "path";
1006
- import { fileURLToPath } from "url";
1007
- var isDevelopment = process.env.NODE_ENV === "development";
1008
- var __filename = fileURLToPath(import.meta.url);
1009
- var __dirname = join(dirname(__filename), !isDevelopment ? "./" : "..");
1010
-
1011
- // src/security/CSP.ts
1012
1023
  var defaultGenerateCSP = (directives, nonce, req) => {
1013
1024
  const merged = { ...directives };
1014
1025
  merged["script-src"] = merged["script-src"] || ["'self'"];
@@ -1232,7 +1243,6 @@ var cspReportPlugin = fp2(
1232
1243
  );
1233
1244
 
1234
1245
  // src/utils/AssetManager.ts
1235
- import { existsSync } from "fs";
1236
1246
  import { readFile } from "fs/promises";
1237
1247
  import path2 from "path";
1238
1248
  import { pathToFileURL } from "url";
@@ -1344,14 +1354,6 @@ var rebuildTemplate = (parts, headContent, bodyContent) => {
1344
1354
  };
1345
1355
 
1346
1356
  // src/utils/AssetManager.ts
1347
- function resolveEntryFile(clientRoot, stem) {
1348
- for (const ext of ENTRY_EXTENSIONS) {
1349
- const filename = `${stem}${ext}`;
1350
- const absPath = path2.join(clientRoot, filename);
1351
- if (existsSync(absPath)) return filename;
1352
- }
1353
- throw new Error(`Entry file "${stem}" not found in ${clientRoot}. Tried: ${ENTRY_EXTENSIONS.map((e) => stem + e).join(", ")}`);
1354
- }
1355
1357
  var createMaps = () => ({
1356
1358
  bootstrapModules: /* @__PURE__ */ new Map(),
1357
1359
  cssLinks: /* @__PURE__ */ new Map(),
@@ -1364,108 +1366,104 @@ var createMaps = () => ({
1364
1366
  var processConfigs = (configs, baseClientRoot, templateDefaults) => {
1365
1367
  return configs.map((config) => {
1366
1368
  const clientRoot = path2.resolve(baseClientRoot, config.entryPoint);
1367
- const entryClient = config.entryClient || templateDefaults.defaultEntryClient;
1368
- const entryServer = config.entryServer || templateDefaults.defaultEntryServer;
1369
- const entryClientFile = resolveEntryFile(clientRoot, entryClient);
1370
- const entryServerFile = resolveEntryFile(clientRoot, entryServer);
1371
1369
  return {
1372
1370
  clientRoot,
1373
1371
  entryPoint: config.entryPoint,
1374
- entryClient,
1375
- entryServer,
1372
+ entryClient: config.entryClient || templateDefaults.defaultEntryClient,
1373
+ // stem
1374
+ entryServer: config.entryServer || templateDefaults.defaultEntryServer,
1375
+ // stem
1376
1376
  htmlTemplate: config.htmlTemplate || templateDefaults.defaultHtmlTemplate,
1377
1377
  appId: config.appId,
1378
- plugins: config.plugins ?? [],
1379
- entryClientFile,
1380
- entryServerFile
1378
+ plugins: config.plugins ?? []
1381
1379
  };
1382
1380
  });
1383
1381
  };
1382
+ var logAssetError = (logger, stage, err) => {
1383
+ if (err instanceof AppError) {
1384
+ logger.error(
1385
+ {
1386
+ error: {
1387
+ name: err.name,
1388
+ message: err.message,
1389
+ stack: err.stack,
1390
+ code: err.code
1391
+ },
1392
+ stage
1393
+ },
1394
+ "Asset load failed"
1395
+ );
1396
+ return;
1397
+ }
1398
+ logger.error(
1399
+ {
1400
+ error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err),
1401
+ stage
1402
+ },
1403
+ "Asset load failed"
1404
+ );
1405
+ };
1406
+ var findManifestEntry = (manifest, stem) => {
1407
+ for (const ext of ENTRY_EXTENSIONS) {
1408
+ const entry = manifest[`${stem}${ext}`];
1409
+ if (entry?.file) return entry;
1410
+ }
1411
+ return null;
1412
+ };
1384
1413
  var loadAssets = async (processedConfigs, baseClientRoot, bootstrapModules, cssLinks, manifests, preloadLinks, renderModules, ssrManifests, templates, opts = {}) => {
1385
- const logger = opts.logger ?? createLogger({
1386
- debug: opts.debug,
1387
- includeContext: true
1388
- });
1414
+ const logger = resolveLogs(opts.logger);
1389
1415
  for (const config of processedConfigs) {
1390
- const { clientRoot, entryClient, entryServer, htmlTemplate, entryPoint, entryClientFile, entryServerFile } = config;
1416
+ const { clientRoot, entryServer, entryClient, htmlTemplate, entryPoint } = config;
1391
1417
  try {
1392
1418
  const templateHtmlPath = path2.join(clientRoot, htmlTemplate);
1393
- const templateHtml = await readFile(templateHtmlPath, "utf-8");
1394
- templates.set(clientRoot, templateHtml);
1419
+ templates.set(clientRoot, await readFile(templateHtmlPath, "utf-8"));
1395
1420
  const relativeBasePath = path2.relative(baseClientRoot, clientRoot).replace(/\\/g, "/");
1396
1421
  const adjustedRelativePath = relativeBasePath ? `/${relativeBasePath}` : "";
1397
- if (!isDevelopment) {
1398
- try {
1399
- const distRoot = path2.dirname(baseClientRoot);
1400
- const ssrRoot = path2.join(distRoot, "ssr");
1401
- const clientDistPath = path2.join(baseClientRoot, entryPoint);
1402
- const manifestPath = path2.join(clientDistPath, ".vite/manifest.json");
1403
- const manifestContent = await readFile(manifestPath, "utf-8");
1404
- const manifest = JSON.parse(manifestContent);
1405
- manifests.set(clientRoot, manifest);
1406
- const ssrDistPath = path2.join(ssrRoot, entryPoint);
1407
- const ssrManifestPath = path2.join(ssrDistPath, ".vite/ssr-manifest.json");
1408
- const ssrManifestContent = await readFile(ssrManifestPath, "utf-8");
1409
- const ssrManifest = JSON.parse(ssrManifestContent);
1410
- ssrManifests.set(clientRoot, ssrManifest);
1411
- const manifestEntry = manifest[entryClientFile];
1412
- if (!manifestEntry?.file) {
1413
- throw AppError.internal(`Entry client file not found in manifest for ${entryClientFile}`, {
1414
- details: {
1415
- clientRoot,
1416
- entryClientFile,
1417
- availableKeys: Object.keys(manifest)
1418
- }
1419
- });
1420
- }
1421
- const bootstrapModule = `/${adjustedRelativePath}/${manifestEntry.file}`.replace(/\/{2,}/g, "/");
1422
- bootstrapModules.set(clientRoot, bootstrapModule);
1423
- const preloadLink = renderPreloadLinks(ssrManifest, adjustedRelativePath);
1424
- preloadLinks.set(clientRoot, preloadLink);
1425
- const cssLink = getCssLinks(manifest, adjustedRelativePath);
1426
- cssLinks.set(clientRoot, cssLink);
1427
- const renderModulePath = path2.join(ssrDistPath, `${entryServer}.js`);
1428
- const moduleUrl = pathToFileURL(renderModulePath).href;
1429
- try {
1430
- const importedModule = await import(moduleUrl);
1431
- renderModules.set(clientRoot, importedModule);
1432
- } catch (err) {
1433
- throw AppError.internal(`Failed to load render module ${renderModulePath}`, {
1434
- cause: err,
1435
- details: { moduleUrl, clientRoot, entryServer, ssrDistPath }
1436
- });
1437
- }
1438
- } catch (err) {
1439
- if (err instanceof AppError) {
1440
- logger.error(
1441
- {
1442
- error: { name: err.name, message: err.message, stack: err.stack, code: err.code },
1443
- stage: "loadAssets:production"
1444
- },
1445
- "Asset load failed"
1446
- );
1447
- } else {
1448
- logger.error(
1449
- {
1450
- error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err),
1451
- stage: "loadAssets:production"
1452
- },
1453
- "Asset load failed"
1454
- );
1422
+ if (isDevelopment) {
1423
+ const bootstrapModule2 = `/${adjustedRelativePath}/${entryClient}`.replace(/\/{2,}/g, "/");
1424
+ bootstrapModules.set(clientRoot, bootstrapModule2);
1425
+ continue;
1426
+ }
1427
+ const distRoot = path2.dirname(baseClientRoot);
1428
+ const ssrRoot = path2.join(distRoot, "ssr");
1429
+ const clientDistPath = path2.join(baseClientRoot, entryPoint);
1430
+ const manifestPath = path2.join(clientDistPath, ".vite/manifest.json");
1431
+ const manifest = JSON.parse(await readFile(manifestPath, "utf-8"));
1432
+ manifests.set(clientRoot, manifest);
1433
+ const ssrDistPath = path2.join(ssrRoot, entryPoint);
1434
+ const ssrManifestPath = path2.join(ssrDistPath, ".vite/ssr-manifest.json");
1435
+ const ssrManifest = JSON.parse(await readFile(ssrManifestPath, "utf-8"));
1436
+ ssrManifests.set(clientRoot, ssrManifest);
1437
+ const manifestEntry = findManifestEntry(manifest, entryClient);
1438
+ if (!manifestEntry?.file) {
1439
+ throw AppError.internal(`Entry "${entryClient}" not found in manifest`, {
1440
+ details: {
1441
+ tried: ENTRY_EXTENSIONS.map((e) => `${entryClient}${e}`),
1442
+ availableKeys: Object.keys(manifest),
1443
+ clientRoot,
1444
+ entryPoint,
1445
+ manifestPath
1455
1446
  }
1456
- }
1457
- } else {
1458
- const bootstrapModule = `/${adjustedRelativePath}/${entryClientFile}`.replace(/\/{2,}/g, "/");
1459
- bootstrapModules.set(clientRoot, bootstrapModule);
1447
+ });
1448
+ }
1449
+ const bootstrapModule = `/${adjustedRelativePath}/${manifestEntry.file}`.replace(/\/{2,}/g, "/");
1450
+ bootstrapModules.set(clientRoot, bootstrapModule);
1451
+ preloadLinks.set(clientRoot, renderPreloadLinks(ssrManifest, adjustedRelativePath));
1452
+ cssLinks.set(clientRoot, getCssLinks(manifest, adjustedRelativePath));
1453
+ const renderModulePath = path2.join(ssrDistPath, `${entryServer}.js`);
1454
+ const moduleUrl = pathToFileURL(renderModulePath).href;
1455
+ try {
1456
+ const importedModule = await import(moduleUrl);
1457
+ renderModules.set(clientRoot, importedModule);
1458
+ } catch (err) {
1459
+ throw AppError.internal(`Failed to load render module ${renderModulePath}`, {
1460
+ cause: err,
1461
+ details: { moduleUrl, clientRoot, entryServer, ssrDistPath }
1462
+ });
1460
1463
  }
1461
1464
  } catch (err) {
1462
- logger.error(
1463
- {
1464
- error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err),
1465
- stage: "loadAssets:config"
1466
- },
1467
- "Failed to process config"
1468
- );
1465
+ logAssetError(logger, isDevelopment ? "loadAssets:development" : "loadAssets:production", err);
1466
+ if (!isDevelopment) throw err;
1469
1467
  }
1470
1468
  }
1471
1469
  };
@@ -1544,27 +1542,284 @@ var setupDevServer = async (app, baseClientRoot, alias, debug, devNet, plugins =
1544
1542
  protocol: "ws"
1545
1543
  }
1546
1544
  }
1547
- });
1548
- overrideCSSHMRConsoleError();
1549
- app.addHook("onRequest", async (request, reply) => {
1550
- await new Promise((resolve) => {
1551
- viteDevServer.middlewares(request.raw, reply.raw, () => {
1552
- if (!reply.sent) resolve();
1553
- });
1554
- });
1555
- });
1556
- return viteDevServer;
1557
- };
1558
-
1559
- // src/utils/HandleRender.ts
1560
- import path4 from "path";
1561
- import { PassThrough } from "stream";
1545
+ });
1546
+ overrideCSSHMRConsoleError();
1547
+ app.addHook("onRequest", async (request, reply) => {
1548
+ await new Promise((resolve) => {
1549
+ viteDevServer.middlewares(request.raw, reply.raw, () => {
1550
+ if (!reply.sent) resolve();
1551
+ });
1552
+ });
1553
+ });
1554
+ return viteDevServer;
1555
+ };
1556
+
1557
+ // src/utils/HandleRender.ts
1558
+ import path5 from "path";
1559
+ import { PassThrough } from "stream";
1560
+
1561
+ // src/core/constants.ts
1562
+ var RENDERTYPE = {
1563
+ ssr: "ssr",
1564
+ streaming: "streaming"
1565
+ };
1566
+ var REGEX2 = {
1567
+ SAFE_TRACE: /^[a-zA-Z0-9-_:.]{1,128}$/
1568
+ };
1569
+
1570
+ // src/Build.ts
1571
+ import { existsSync } from "fs";
1572
+ import * as fs from "fs";
1573
+ import path4 from "path";
1574
+ import { build } from "vite";
1575
+ function resolveInputs(isSSRBuild, mainExists, paths) {
1576
+ if (isSSRBuild) return { server: paths.server };
1577
+ if (mainExists) return { client: paths.client, main: paths.main };
1578
+ return { client: paths.client };
1579
+ }
1580
+ function resolveEntryFile(clientRoot, stem, exists = fs.existsSync) {
1581
+ for (const ext of ENTRY_EXTENSIONS) {
1582
+ const filename = `${stem}${ext}`;
1583
+ if (exists(path4.join(clientRoot, filename))) return filename;
1584
+ }
1585
+ throw new Error(`Entry file "${stem}" not found in ${clientRoot}. Tried: ${ENTRY_EXTENSIONS.map((e) => stem + e).join(", ")}`);
1586
+ }
1587
+ function getFrameworkInvariants(config) {
1588
+ return {
1589
+ root: config.root || "",
1590
+ base: config.base || "/",
1591
+ publicDir: config.publicDir === void 0 ? "public" : config.publicDir,
1592
+ build: {
1593
+ outDir: config.build?.outDir || "",
1594
+ manifest: config.build?.manifest ?? false,
1595
+ ssr: config.build?.ssr ?? void 0,
1596
+ // Preserve exact type
1597
+ ssrManifest: config.build?.ssrManifest ?? false,
1598
+ format: config.build?.format,
1599
+ target: config.build?.target,
1600
+ rollupOptions: {
1601
+ input: config.build?.rollupOptions?.input || {}
1602
+ }
1603
+ }
1604
+ };
1605
+ }
1606
+ var normalisePlugins = (p) => Array.isArray(p) ? p : p ? [p] : [];
1607
+ function mergeViteConfig(framework, userOverride, context) {
1608
+ if (!userOverride) return framework;
1609
+ const userConfig = typeof userOverride === "function" && context ? userOverride(context) : userOverride;
1610
+ const invariants = getFrameworkInvariants(framework);
1611
+ const merged = {
1612
+ ...framework,
1613
+ build: { ...framework.build ?? {} },
1614
+ css: { ...framework.css ?? {} },
1615
+ resolve: { ...framework.resolve ?? {} },
1616
+ plugins: [...framework.plugins ?? []],
1617
+ define: { ...framework.define ?? {} }
1618
+ };
1619
+ const ignoredKeys = [];
1620
+ if (userConfig.plugins) merged.plugins = [...normalisePlugins(merged.plugins), ...normalisePlugins(userConfig.plugins)];
1621
+ if (userConfig.define && typeof userConfig.define === "object") merged.define = { ...merged.define, ...userConfig.define };
1622
+ if (userConfig.css?.preprocessorOptions) {
1623
+ const fpp = merged.css?.preprocessorOptions ?? {};
1624
+ const upp = userConfig.css.preprocessorOptions;
1625
+ merged.css ??= {};
1626
+ merged.css.preprocessorOptions ??= {};
1627
+ merged.css.preprocessorOptions = Object.keys({ ...fpp, ...upp }).reduce((acc, engine) => {
1628
+ acc[engine] = {
1629
+ ...fpp[engine],
1630
+ ...upp[engine]
1631
+ };
1632
+ return acc;
1633
+ }, {});
1634
+ }
1635
+ if (userConfig.build) {
1636
+ const protectedBuildFields = ["outDir", "ssr", "ssrManifest", "format", "target"];
1637
+ for (const field of protectedBuildFields) {
1638
+ if (field in userConfig.build) ignoredKeys.push(`build.${field}`);
1639
+ }
1640
+ if ("sourcemap" in userConfig.build) merged.build.sourcemap = userConfig.build.sourcemap;
1641
+ if ("minify" in userConfig.build) merged.build.minify = userConfig.build.minify;
1642
+ if (userConfig.build.terserOptions) {
1643
+ merged.build.terserOptions = {
1644
+ ...merged.build.terserOptions,
1645
+ ...userConfig.build.terserOptions
1646
+ };
1647
+ }
1648
+ if (userConfig.build.rollupOptions) {
1649
+ const userRollup = userConfig.build.rollupOptions;
1650
+ const ro = merged.build.rollupOptions ??= {};
1651
+ if ("input" in userRollup) ignoredKeys.push("build.rollupOptions.input");
1652
+ if ("external" in userRollup) ro.external = userRollup.external;
1653
+ if (userRollup.output) {
1654
+ const uo = Array.isArray(userRollup.output) ? userRollup.output[0] : userRollup.output;
1655
+ ro.output = {
1656
+ ...Array.isArray(ro.output) ? ro.output[0] : ro.output,
1657
+ ...uo?.manualChunks ? { manualChunks: uo.manualChunks } : {}
1658
+ };
1659
+ }
1660
+ }
1661
+ }
1662
+ if (userConfig.resolve) {
1663
+ const { alias: _ignore, ...rest } = userConfig.resolve;
1664
+ if (_ignore) ignoredKeys.push("resolve.alias");
1665
+ merged.resolve = { ...merged.resolve, ...rest };
1666
+ }
1667
+ if (userConfig.server) ignoredKeys.push("server");
1668
+ if ("root" in userConfig) ignoredKeys.push("root");
1669
+ if ("base" in userConfig) ignoredKeys.push("base");
1670
+ if ("publicDir" in userConfig) ignoredKeys.push("publicDir");
1671
+ for (const key of ["esbuild", "logLevel", "envPrefix", "optimizeDeps", "ssr"]) {
1672
+ if (key in userConfig) merged[key] = userConfig[key];
1673
+ }
1674
+ merged.root = invariants.root;
1675
+ merged.base = invariants.base;
1676
+ merged.publicDir = invariants.publicDir;
1677
+ merged.build.outDir = invariants.build.outDir;
1678
+ merged.build.manifest = invariants.build.manifest;
1679
+ merged.build.ssr = invariants.build.ssr;
1680
+ merged.build.ssrManifest = invariants.build.ssrManifest;
1681
+ merged.build.format = invariants.build.format;
1682
+ merged.build.target = invariants.build.target;
1683
+ if (invariants.build.ssr === void 0) delete merged.build.ssr;
1684
+ if (invariants.build.format === void 0) delete merged.build.format;
1685
+ if (invariants.build.target === void 0) delete merged.build.target;
1686
+ (merged.build.rollupOptions ??= {}).input = invariants.build.rollupOptions.input;
1687
+ if (ignoredKeys.length > 0) {
1688
+ const prefix = context ? `[taujs:build:${context.entryPoint}]` : "[taujs:build]";
1689
+ console.warn(`${prefix} Ignored Vite config overrides: ${[...new Set(ignoredKeys)].join(", ")}`);
1690
+ }
1691
+ return merged;
1692
+ }
1693
+ function resolveAppFilter(argv, env) {
1694
+ const read = (keys) => {
1695
+ const end = argv.indexOf("--");
1696
+ const limit = end === -1 ? argv.length : end;
1697
+ for (let i = 0; i < limit; i++) {
1698
+ const arg = argv[i];
1699
+ if (!arg) continue;
1700
+ for (const key of keys) {
1701
+ if (arg === key) {
1702
+ const next = argv[i + 1];
1703
+ if (!next || next.startsWith("-")) return "";
1704
+ return next.trim();
1705
+ }
1706
+ const pref = `${key}=`;
1707
+ if (arg.startsWith(pref)) {
1708
+ const v = arg.slice(pref.length).trim();
1709
+ return v;
1710
+ }
1711
+ }
1712
+ }
1713
+ return void 0;
1714
+ };
1715
+ const envFilter = env.TAUJS_APP || env.TAUJS_APPS;
1716
+ const cliFilter = read(["--app", "--apps", "-a"]);
1717
+ const raw = (cliFilter ?? envFilter)?.trim() || void 0;
1718
+ if (!raw) return { selectedIds: null, raw: void 0 };
1719
+ const selectedIds = new Set(
1720
+ raw.split(",").map((s) => s.trim()).filter(Boolean)
1721
+ );
1722
+ return { selectedIds, raw };
1723
+ }
1724
+ async function taujsBuild({
1725
+ config,
1726
+ projectRoot,
1727
+ clientBaseDir,
1728
+ isSSRBuild = process.env.BUILD_MODE === "ssr",
1729
+ alias: userAlias,
1730
+ vite: userViteConfig
1731
+ }) {
1732
+ const deleteDist = async () => {
1733
+ const { rm } = await import("fs/promises");
1734
+ const distPath = path4.resolve(projectRoot, "dist");
1735
+ try {
1736
+ await rm(distPath, { recursive: true, force: true });
1737
+ console.log("Deleted the dist directory\n");
1738
+ } catch (err) {
1739
+ console.error("Error deleting dist directory:", err);
1740
+ }
1741
+ };
1742
+ const extractedConfigs = extractBuildConfigs(config);
1743
+ const processedConfigs = processConfigs(extractedConfigs, clientBaseDir, TEMPLATE);
1744
+ const { selectedIds, raw: appFilterRaw } = resolveAppFilter(process.argv.slice(2), process.env);
1745
+ const configsToBuild = selectedIds ? processedConfigs.filter(({ appId, entryPoint }) => selectedIds.has(appId) || selectedIds.has(entryPoint)) : processedConfigs;
1746
+ if (selectedIds && configsToBuild.length === 0) {
1747
+ console.error(
1748
+ `[taujs:build] No apps match filter "${appFilterRaw}". Known apps: ${processedConfigs.map((c) => `${c.appId}${c.entryPoint ? ` (entry: ${c.entryPoint})` : ""}`).join(", ")}`
1749
+ );
1750
+ process.exit(1);
1751
+ }
1752
+ if (!isSSRBuild) await deleteDist();
1753
+ for (const appConfig of configsToBuild) {
1754
+ const { appId, entryPoint, clientRoot, entryClient, entryServer, htmlTemplate, plugins = [] } = appConfig;
1755
+ const outDir = path4.resolve(projectRoot, isSSRBuild ? `dist/ssr/${entryPoint}` : `dist/client/${entryPoint}`);
1756
+ const root = entryPoint ? path4.resolve(clientBaseDir, entryPoint) : clientBaseDir;
1757
+ const defaultAlias = {
1758
+ "@client": root,
1759
+ "@server": path4.resolve(projectRoot, "src/server"),
1760
+ "@shared": path4.resolve(projectRoot, "src/shared")
1761
+ };
1762
+ const resolvedAlias = { ...defaultAlias, ...userAlias ?? {} };
1763
+ const entryClientFile = resolveEntryFile(clientRoot, entryClient);
1764
+ const entryServerFile = resolveEntryFile(clientRoot, entryServer);
1765
+ const server = path4.resolve(clientRoot, entryServerFile);
1766
+ const client = path4.resolve(clientRoot, entryClientFile);
1767
+ const main = path4.resolve(clientRoot, htmlTemplate);
1768
+ const inputs = resolveInputs(isSSRBuild, !isSSRBuild && existsSync(main), { server, client, main });
1769
+ const nodeVersion = process.versions.node.split(".")[0];
1770
+ const frameworkConfig = {
1771
+ base: entryPoint ? `/${entryPoint}/` : "/",
1772
+ build: {
1773
+ outDir,
1774
+ emptyOutDir: true,
1775
+ manifest: !isSSRBuild,
1776
+ rollupOptions: {
1777
+ input: inputs
1778
+ },
1779
+ ssr: isSSRBuild ? server : void 0,
1780
+ ssrManifest: isSSRBuild,
1781
+ ...isSSRBuild && {
1782
+ format: "esm",
1783
+ target: `node${nodeVersion}`,
1784
+ copyPublicDir: false
1785
+ }
1786
+ },
1787
+ css: {
1788
+ preprocessorOptions: {
1789
+ scss: { api: "modern-compiler" }
1790
+ }
1791
+ },
1792
+ plugins,
1793
+ publicDir: isSSRBuild ? false : "public",
1794
+ resolve: { alias: resolvedAlias },
1795
+ root
1796
+ };
1797
+ const buildContext = {
1798
+ appId,
1799
+ entryPoint,
1800
+ isSSRBuild,
1801
+ clientRoot
1802
+ };
1803
+ const finalConfig = mergeViteConfig(frameworkConfig, userViteConfig, buildContext);
1804
+ try {
1805
+ const mode = isSSRBuild ? "SSR" : "Client";
1806
+ console.log(`[taujs:build:${entryPoint}] Building \u2192 ${mode}`);
1807
+ await build(finalConfig);
1808
+ console.log(`[taujs:build:${entryPoint}] \u2713 Complete
1809
+ `);
1810
+ } catch (error) {
1811
+ console.error(`[taujs:build:${entryPoint}] \u2717 Failed
1812
+ `, error);
1813
+ process.exit(1);
1814
+ }
1815
+ }
1816
+ }
1562
1817
 
1563
1818
  // src/utils/Telemetry.ts
1564
1819
  import crypto2 from "crypto";
1565
1820
  function createRequestContext(req, reply, baseLogger) {
1566
1821
  const raw = typeof req.headers["x-trace-id"] === "string" ? req.headers["x-trace-id"] : "";
1567
- const traceId = raw && REGEX.SAFE_TRACE.test(raw) ? raw : typeof req.id === "string" ? req.id : crypto2.randomUUID();
1822
+ const traceId = raw && REGEX2.SAFE_TRACE.test(raw) ? raw : typeof req.id === "string" ? req.id : crypto2.randomUUID();
1568
1823
  reply.header("x-trace-id", traceId);
1569
1824
  const anyLogger = baseLogger;
1570
1825
  const child = anyLogger.child;
@@ -1615,7 +1870,7 @@ var handleRender = async (req, reply, routeMatchers, processedConfigs, serviceRe
1615
1870
  }
1616
1871
  });
1617
1872
  }
1618
- const { clientRoot, entryServerFile } = config;
1873
+ const { clientRoot, entryServer } = config;
1619
1874
  let template = ensureNonNull(maps.templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
1620
1875
  const bootstrapModule = maps.bootstrapModules.get(clientRoot);
1621
1876
  const cssLink = maps.cssLinks.get(clientRoot);
@@ -1627,7 +1882,8 @@ var handleRender = async (req, reply, routeMatchers, processedConfigs, serviceRe
1627
1882
  try {
1628
1883
  template = template.replace(/<script type="module" src="\/@vite\/client"><\/script>/g, "");
1629
1884
  template = template.replace(/<style type="text\/css">[\s\S]*?<\/style>/g, "");
1630
- const entryServerPath = path4.join(clientRoot, entryServerFile);
1885
+ const entryServerFile = resolveEntryFile(clientRoot, entryServer);
1886
+ const entryServerPath = path5.join(clientRoot, entryServerFile);
1631
1887
  const executedModule = await viteDevServer.ssrLoadModule(entryServerPath);
1632
1888
  renderModule = executedModule;
1633
1889
  const styles = await collectStyle(viteDevServer, [entryServerPath]);
@@ -1635,7 +1891,7 @@ var handleRender = async (req, reply, routeMatchers, processedConfigs, serviceRe
1635
1891
  template = template?.replace("</head>", `<style type="text/css"${styleNonce}>${styles}</style></head>`);
1636
1892
  template = await viteDevServer.transformIndexHtml(url, template);
1637
1893
  } catch (error) {
1638
- throw AppError.internal("Failed to load dev assets", { cause: error, details: { clientRoot, entryServerFile, url } });
1894
+ throw AppError.internal("Failed to load dev assets", { cause: error, details: { clientRoot, entryServer, url } });
1639
1895
  }
1640
1896
  } else {
1641
1897
  renderModule = maps.renderModules.get(clientRoot);
@@ -1904,28 +2160,30 @@ var handleNotFound = async (req, reply, processedConfigs, maps, opts = {}) => {
1904
2160
  }
1905
2161
  };
1906
2162
 
1907
- // src/utils/ResolveRouteData.ts
1908
- async function resolveRouteData(url, opts) {
1909
- const { req, reply, routeMatchers, serviceRegistry, logger } = opts;
1910
- const match2 = matchRoute(url, routeMatchers);
2163
+ // src/core/routes/ResolveRouteData.ts
2164
+ async function resolveRouteDataCore(url, opts) {
2165
+ const match2 = matchRoute(url, opts.routeMatchers);
1911
2166
  if (!match2) {
1912
- throw AppError.notFound("route_not_found", {
1913
- details: { url }
1914
- });
2167
+ throw AppError.notFound("route_not_found", { details: { url } });
1915
2168
  }
1916
2169
  const { route, params } = match2;
1917
- const attr = route.attr;
1918
- if (!attr?.data) {
2170
+ if (!route.attr?.data) {
1919
2171
  throw AppError.notFound("no_data_handler", {
1920
- details: {
1921
- url,
1922
- path: route.path,
1923
- appId: route.appId
1924
- }
2172
+ details: { url, path: route.path, appId: route.appId }
1925
2173
  });
1926
2174
  }
1927
- const ctx = createRequestContext(req, reply, logger);
1928
- return fetchInitialData(attr, params, serviceRegistry, ctx);
2175
+ const ctx = opts.getCtx();
2176
+ return fetchInitialData(route.attr, params, opts.serviceRegistry, ctx);
2177
+ }
2178
+
2179
+ // src/utils/ResolveRouteData.ts
2180
+ async function resolveRouteData(url, opts) {
2181
+ const { req, reply, routeMatchers, serviceRegistry, logger } = opts;
2182
+ return resolveRouteDataCore(url, {
2183
+ routeMatchers,
2184
+ serviceRegistry,
2185
+ getCtx: () => createRequestContext(req, reply, logger)
2186
+ });
1929
2187
  }
1930
2188
 
1931
2189
  // src/utils/StaticAssets.ts
@@ -2000,10 +2258,7 @@ var SSRServer = fp3(
2000
2258
  maps.renderModules,
2001
2259
  maps.ssrManifests,
2002
2260
  maps.templates,
2003
- {
2004
- debug: opts.debug,
2005
- logger
2006
- }
2261
+ { logger }
2007
2262
  );
2008
2263
  if (!isDevelopment && !opts.staticAssets) {
2009
2264
  const fastifyStatic = await import("@fastify/static");
@@ -2111,13 +2366,13 @@ var SSRServer = fp3(
2111
2366
 
2112
2367
  // src/CreateServer.ts
2113
2368
  var resolveClientRoot = (userClientRoot) => {
2114
- if (userClientRoot) return path5.isAbsolute(userClientRoot) ? userClientRoot : path5.resolve(process.cwd(), userClientRoot);
2369
+ if (userClientRoot) return path6.isAbsolute(userClientRoot) ? userClientRoot : path6.resolve(process.cwd(), userClientRoot);
2115
2370
  const cwd = process.cwd();
2116
- if (process.env.NODE_ENV === "production") return path5.resolve(cwd, "dist/client");
2117
- return path5.resolve(cwd, "src/client");
2371
+ if (process.env.NODE_ENV === "production") return path6.resolve(cwd, "dist/client");
2372
+ return path6.resolve(cwd, "src/client");
2118
2373
  };
2119
2374
  var createServer = async (opts) => {
2120
- const t0 = performance3.now();
2375
+ const t0 = performance.now();
2121
2376
  const clientRoot = resolveClientRoot(opts.clientRoot);
2122
2377
  const app = opts.fastify ?? Fastify({ logger: false });
2123
2378
  const fastifyLogger = app.log && app.log.level && app.log.level !== "silent" ? app.log : void 0;
@@ -2178,7 +2433,7 @@ var createServer = async (opts) => {
2178
2433
  "Failed to register SSRServer"
2179
2434
  );
2180
2435
  }
2181
- const t1 = performance3.now();
2436
+ const t1 = performance.now();
2182
2437
  console.log(`
2183
2438
  ${import_picocolors4.default.bgGreen(import_picocolors4.default.black(` ${CONTENT.TAG} `))} configured in ${(t1 - t0).toFixed(0)}ms
2184
2439
  `);
@@ -2186,264 +2441,11 @@ ${import_picocolors4.default.bgGreen(import_picocolors4.default.black(` ${CONTEN
2186
2441
  return { app, net };
2187
2442
  };
2188
2443
 
2189
- // src/Build.ts
2190
- import { existsSync as existsSync2 } from "fs";
2191
- import path6 from "path";
2192
- import { build } from "vite";
2193
- function resolveInputs(isSSRBuild, mainExists, paths) {
2194
- if (isSSRBuild) return { server: paths.server };
2195
- if (mainExists) return { client: paths.client, main: paths.main };
2196
- return { client: paths.client };
2197
- }
2198
- function getFrameworkInvariants(config) {
2199
- return {
2200
- root: config.root || "",
2201
- base: config.base || "/",
2202
- publicDir: config.publicDir === void 0 ? "public" : config.publicDir,
2203
- build: {
2204
- outDir: config.build?.outDir || "",
2205
- manifest: config.build?.manifest ?? false,
2206
- ssr: config.build?.ssr ?? void 0,
2207
- // Preserve exact type
2208
- ssrManifest: config.build?.ssrManifest ?? false,
2209
- format: config.build?.format,
2210
- target: config.build?.target,
2211
- rollupOptions: {
2212
- input: config.build?.rollupOptions?.input || {}
2213
- }
2214
- }
2215
- };
2216
- }
2217
- function mergeViteConfig(framework, userOverride, context) {
2218
- if (!userOverride) return framework;
2219
- const userConfig = typeof userOverride === "function" && context ? userOverride(context) : userOverride;
2220
- const invariants = getFrameworkInvariants(framework);
2221
- const merged = {
2222
- ...framework,
2223
- build: { ...framework.build ?? {} },
2224
- css: { ...framework.css ?? {} },
2225
- resolve: { ...framework.resolve ?? {} },
2226
- plugins: [...framework.plugins ?? []],
2227
- define: { ...framework.define ?? {} }
2228
- };
2229
- const ignoredKeys = [];
2230
- if (userConfig.plugins) {
2231
- const frameworkPlugins = merged.plugins;
2232
- merged.plugins = [...frameworkPlugins, ...userConfig.plugins];
2233
- }
2234
- if (userConfig.define && typeof userConfig.define === "object") {
2235
- merged.define = {
2236
- ...merged.define,
2237
- ...userConfig.define
2238
- };
2239
- }
2240
- if (userConfig.css?.preprocessorOptions && typeof userConfig.css.preprocessorOptions === "object") {
2241
- const fpp = merged.css.preprocessorOptions ?? {};
2242
- const upp = userConfig.css.preprocessorOptions;
2243
- merged.css.preprocessorOptions = Object.keys({ ...fpp, ...upp }).reduce((acc, engine) => {
2244
- const fppEngine = fpp[engine];
2245
- const uppEngine = upp[engine];
2246
- acc[engine] = { ...fppEngine ?? {}, ...uppEngine ?? {} };
2247
- return acc;
2248
- }, {});
2249
- }
2250
- if (userConfig.build) {
2251
- const protectedBuildFields = ["outDir", "ssr", "ssrManifest", "format", "target"];
2252
- for (const field of protectedBuildFields) {
2253
- if (field in userConfig.build) {
2254
- ignoredKeys.push(`build.${field}`);
2255
- }
2256
- }
2257
- if ("sourcemap" in userConfig.build) merged.build.sourcemap = userConfig.build.sourcemap;
2258
- if ("minify" in userConfig.build) merged.build.minify = userConfig.build.minify;
2259
- if (userConfig.build.terserOptions && typeof userConfig.build.terserOptions === "object") {
2260
- merged.build.terserOptions = {
2261
- ...merged.build.terserOptions ?? {},
2262
- ...userConfig.build.terserOptions
2263
- };
2264
- }
2265
- if (userConfig.build.rollupOptions) {
2266
- if (!merged.build.rollupOptions) {
2267
- merged.build.rollupOptions = {};
2268
- }
2269
- const userRollup = userConfig.build.rollupOptions;
2270
- if ("input" in userRollup) ignoredKeys.push("build.rollupOptions.input");
2271
- if ("external" in userRollup) merged.build.rollupOptions.external = userRollup.external;
2272
- if (userRollup.output) {
2273
- const mro = merged.build.rollupOptions ??= {};
2274
- const uo = Array.isArray(userRollup.output) ? userRollup.output[0] : userRollup.output;
2275
- const baseOut = Array.isArray(mro.output) ? mro.output[0] ?? {} : mro.output ?? {};
2276
- mro.output = { ...baseOut, ...uo?.manualChunks ? { manualChunks: uo.manualChunks } : {} };
2277
- }
2278
- }
2279
- }
2280
- if (userConfig.resolve) {
2281
- const userResolve = userConfig.resolve;
2282
- const { alias: _ignore, ...resolveRest } = userResolve;
2283
- if (_ignore) ignoredKeys.push("resolve.alias");
2284
- merged.resolve = {
2285
- ...merged.resolve,
2286
- ...resolveRest
2287
- };
2288
- }
2289
- if (userConfig.server) ignoredKeys.push("server (ignored in build; dev-only)");
2290
- if ("root" in userConfig) ignoredKeys.push("root");
2291
- if ("base" in userConfig) ignoredKeys.push("base");
2292
- if ("publicDir" in userConfig) ignoredKeys.push("publicDir");
2293
- const safeTopLevelKeys = /* @__PURE__ */ new Set([
2294
- "esbuild",
2295
- "logLevel",
2296
- "envPrefix",
2297
- "optimizeDeps",
2298
- "ssr"
2299
- // NOTE: NOT 'server' (build-time irrelevant; dev-server only)
2300
- ]);
2301
- for (const [key, value] of Object.entries(userConfig)) {
2302
- if (safeTopLevelKeys.has(key)) merged[key] = value;
2303
- }
2304
- merged.root = invariants.root;
2305
- merged.base = invariants.base;
2306
- merged.publicDir = invariants.publicDir;
2307
- merged.build.outDir = invariants.build.outDir;
2308
- merged.build.manifest = invariants.build.manifest;
2309
- if (invariants.build.ssr !== void 0) merged.build.ssr = invariants.build.ssr;
2310
- merged.build.ssrManifest = invariants.build.ssrManifest;
2311
- if (invariants.build.format) merged.build.format = invariants.build.format;
2312
- if (invariants.build.target) merged.build.target = invariants.build.target;
2313
- if (!merged.build.rollupOptions) merged.build.rollupOptions = {};
2314
- merged.build.rollupOptions.input = invariants.build.rollupOptions.input;
2315
- if (ignoredKeys.length > 0) {
2316
- const uniqueKeys = [...new Set(ignoredKeys)];
2317
- const prefix = context ? `[taujs:build:${context.entryPoint}]` : "[taujs:build]";
2318
- console.warn(`${prefix} Ignored Vite config overrides: ${uniqueKeys.join(", ")}`);
2319
- }
2320
- return merged;
2321
- }
2322
- function resolveAppFilter(argv, env) {
2323
- const read = (keys) => {
2324
- const end = argv.indexOf("--");
2325
- const limit = end === -1 ? argv.length : end;
2326
- for (let i = 0; i < limit; i++) {
2327
- const arg = argv[i];
2328
- if (!arg) continue;
2329
- for (const key of keys) {
2330
- if (arg === key) {
2331
- const next = argv[i + 1];
2332
- if (!next || next.startsWith("-")) return "";
2333
- return next.trim();
2334
- }
2335
- const pref = `${key}=`;
2336
- if (arg.startsWith(pref)) {
2337
- const v = arg.slice(pref.length).trim();
2338
- return v;
2339
- }
2340
- }
2341
- }
2342
- return void 0;
2343
- };
2344
- const envFilter = env.TAUJS_APP || env.TAUJS_APPS;
2345
- const cliFilter = read(["--app", "--apps", "-a"]);
2346
- const raw = (cliFilter ?? envFilter)?.trim() || void 0;
2347
- if (!raw) return { selectedIds: null, raw: void 0 };
2348
- const selectedIds = new Set(
2349
- raw.split(",").map((s) => s.trim()).filter(Boolean)
2350
- );
2351
- return { selectedIds, raw };
2352
- }
2353
- async function taujsBuild({
2354
- config,
2355
- projectRoot,
2356
- clientBaseDir,
2357
- isSSRBuild = process.env.BUILD_MODE === "ssr",
2358
- alias: userAlias,
2359
- vite: userViteConfig
2360
- }) {
2361
- const deleteDist = async () => {
2362
- const { rm } = await import("fs/promises");
2363
- const distPath = path6.resolve(projectRoot, "dist");
2364
- try {
2365
- await rm(distPath, { recursive: true, force: true });
2366
- console.log("Deleted the dist directory\n");
2367
- } catch (err) {
2368
- console.error("Error deleting dist directory:", err);
2369
- }
2370
- };
2371
- const extractedConfigs = extractBuildConfigs(config);
2372
- const processedConfigs = processConfigs(extractedConfigs, clientBaseDir, TEMPLATE);
2373
- const { selectedIds, raw: appFilterRaw } = resolveAppFilter(process.argv.slice(2), process.env);
2374
- const configsToBuild = selectedIds ? processedConfigs.filter(({ appId, entryPoint }) => selectedIds.has(appId) || selectedIds.has(entryPoint)) : processedConfigs;
2375
- if (selectedIds && configsToBuild.length === 0) {
2376
- console.error(
2377
- `[taujs:build] No apps match filter "${appFilterRaw}". Known apps: ${processedConfigs.map((c) => `${c.appId}${c.entryPoint ? ` (entry: ${c.entryPoint})` : ""}`).join(", ")}`
2378
- );
2379
- process.exit(1);
2380
- }
2381
- if (!isSSRBuild) await deleteDist();
2382
- for (const appConfig of configsToBuild) {
2383
- const { appId, entryPoint, clientRoot, entryClientFile, entryServerFile, htmlTemplate, plugins = [] } = appConfig;
2384
- const outDir = path6.resolve(projectRoot, isSSRBuild ? `dist/ssr/${entryPoint}` : `dist/client/${entryPoint}`);
2385
- const root = entryPoint ? path6.resolve(clientBaseDir, entryPoint) : clientBaseDir;
2386
- const defaultAlias = {
2387
- "@client": root,
2388
- "@server": path6.resolve(projectRoot, "src/server"),
2389
- "@shared": path6.resolve(projectRoot, "src/shared")
2390
- };
2391
- const resolvedAlias = { ...defaultAlias, ...userAlias ?? {} };
2392
- const server = path6.resolve(clientRoot, entryServerFile);
2393
- const client = path6.resolve(clientRoot, entryClientFile);
2394
- const main = path6.resolve(clientRoot, htmlTemplate);
2395
- const inputs = resolveInputs(isSSRBuild, !isSSRBuild && existsSync2(main), { server, client, main });
2396
- const nodeVersion = process.versions.node.split(".")[0];
2397
- const frameworkConfig = {
2398
- base: entryPoint ? `/${entryPoint}/` : "/",
2399
- build: {
2400
- outDir,
2401
- emptyOutDir: true,
2402
- manifest: !isSSRBuild,
2403
- rollupOptions: {
2404
- input: inputs
2405
- },
2406
- ssr: isSSRBuild ? server : void 0,
2407
- ssrManifest: isSSRBuild,
2408
- ...isSSRBuild && {
2409
- format: "esm",
2410
- target: `node${nodeVersion}`,
2411
- copyPublicDir: false
2412
- }
2413
- },
2414
- css: {
2415
- preprocessorOptions: {
2416
- scss: { api: "modern-compiler" }
2417
- }
2418
- },
2419
- plugins,
2420
- publicDir: isSSRBuild ? false : "public",
2421
- resolve: { alias: resolvedAlias },
2422
- root
2423
- };
2424
- const buildContext = {
2425
- appId,
2426
- entryPoint,
2427
- isSSRBuild,
2428
- clientRoot
2429
- };
2430
- const finalConfig = mergeViteConfig(frameworkConfig, userViteConfig, buildContext);
2431
- try {
2432
- const mode = isSSRBuild ? "SSR" : "Client";
2433
- console.log(`[taujs:build:${entryPoint}] Building \u2192 ${mode}`);
2434
- await build(finalConfig);
2435
- console.log(`[taujs:build:${entryPoint}] \u2713 Complete
2436
- `);
2437
- } catch (error) {
2438
- console.error(`[taujs:build:${entryPoint}] \u2717 Failed
2439
- `, error);
2440
- process.exit(1);
2441
- }
2442
- }
2443
- }
2444
-
2445
2444
  // src/logging/Adapters.ts
2446
- var cleanMeta = (m) => m && Object.keys(m).length === 0 ? void 0 : m;
2445
+ var cleanMeta = (meta) => {
2446
+ if (meta && typeof meta === "object" && !Array.isArray(meta)) return Object.keys(meta).length ? meta : void 0;
2447
+ return void 0;
2448
+ };
2447
2449
  function messageMetaAdapter(sink) {
2448
2450
  return {
2449
2451
  debug: (meta, message) => sink.debug?.(message, cleanMeta(meta)),