@taujs/server 0.4.1 → 0.4.2

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.
@@ -6,6 +6,7 @@ type DebugCategory = (typeof DEBUG_CATEGORIES)[number];
6
6
  type DebugConfig = boolean | DebugCategory[] | ({
7
7
  all?: boolean;
8
8
  } & Partial<Record<DebugCategory, boolean>>);
9
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error';
9
10
  interface BaseLogger {
10
11
  debug?(meta?: Record<string, unknown>, message?: string): void;
11
12
  info?(meta?: Record<string, unknown>, message?: string): void;
@@ -22,17 +23,51 @@ interface Logs extends BaseLogger {
22
23
  child(context: Record<string, unknown>): Logs;
23
24
  isDebugEnabled(category: DebugCategory): boolean;
24
25
  }
26
+ declare class Logger implements Logs {
27
+ private config;
28
+ private debugEnabled;
29
+ private context;
30
+ constructor(config?: {
31
+ custom?: BaseLogger;
32
+ context?: Record<string, unknown>;
33
+ minLevel?: LogLevel;
34
+ includeStack?: boolean | ((level: LogLevel) => boolean);
35
+ includeContext?: boolean | ((level: LogLevel) => boolean);
36
+ singleLine?: boolean;
37
+ });
38
+ child(context: Record<string, unknown>): Logger;
39
+ configure(debug?: DebugConfig): void;
40
+ isDebugEnabled(category: DebugCategory): boolean;
41
+ private shouldEmit;
42
+ private shouldIncludeStack;
43
+ private stripStacks;
44
+ private formatTimestamp;
45
+ private emit;
46
+ info(meta?: unknown, message?: string): void;
47
+ warn(meta?: unknown, message?: string): void;
48
+ error(meta?: unknown, message?: string): void;
49
+ debug(category: DebugCategory, meta?: unknown, message?: string): void;
50
+ }
51
+ declare function createLogger(opts?: {
52
+ debug?: DebugConfig | string | boolean;
53
+ custom?: BaseLogger;
54
+ context?: Record<string, unknown>;
55
+ minLevel?: LogLevel;
56
+ includeStack?: boolean | ((level: LogLevel) => boolean);
57
+ includeContext?: boolean | ((level: LogLevel) => boolean);
58
+ singleLine?: boolean;
59
+ }): Logger;
25
60
 
26
- type Schema<T> = (input: unknown) => T;
27
- type LooseSpec = Readonly<Record<string, ServiceMethod<any, Record<string, unknown>> | {
28
- handler: ServiceMethod<any, Record<string, unknown>>;
29
- params?: Schema<any>;
30
- result?: Schema<any>;
31
- parsers?: {
32
- params?: Schema<any>;
33
- result?: Schema<any>;
34
- };
35
- }>>;
61
+ type JsonPrimitive = string | number | boolean | null;
62
+ type JsonValue = JsonPrimitive | JsonValue[] | {
63
+ [k: string]: JsonValue;
64
+ };
65
+ type JsonObject = {
66
+ [k: string]: JsonValue;
67
+ };
68
+ type NarrowSchema<T> = {
69
+ parse: (u: unknown) => T;
70
+ } | ((u: unknown) => T);
36
71
  type ServiceContext = {
37
72
  signal?: AbortSignal;
38
73
  deadlineMs?: number;
@@ -43,18 +78,21 @@ type ServiceContext = {
43
78
  roles: string[];
44
79
  } | null;
45
80
  };
46
- type ServiceMethod<P, R extends Record<string, unknown>> = (params: P, ctx: ServiceContext) => Promise<R>;
47
- type ServiceMethodDescriptor<P, R extends Record<string, unknown>> = {
48
- handler: ServiceMethod<P, R>;
49
- parsers?: {
50
- params?: Schema<P>;
51
- result?: Schema<R>;
52
- };
81
+ type ServiceMethod<P, R extends JsonObject = JsonObject> = (params: P, ctx: ServiceContext) => Promise<R>;
82
+ type ServiceDefinition = Readonly<Record<string, ServiceMethod<any, JsonObject>>>;
83
+ type ServiceRegistry = Readonly<Record<string, ServiceDefinition>>;
84
+ type ServiceDescriptor = {
85
+ serviceName: string;
86
+ serviceMethod: string;
87
+ args?: JsonObject;
53
88
  };
54
- type ServiceRegistry = Readonly<Record<string, Readonly<Record<string, ServiceMethodDescriptor<any, Record<string, unknown>>>>>>;
55
- declare const defineService: <T extends LooseSpec>(spec: T) => { [K in keyof T]: T[K] extends ServiceMethod<infer P, infer R> ? ServiceMethodDescriptor<P, R> : T[K] extends {
89
+ declare function defineService<T extends Record<string, ServiceMethod<any, JsonObject> | {
90
+ handler: ServiceMethod<any, JsonObject>;
91
+ params?: NarrowSchema<any>;
92
+ result?: NarrowSchema<any>;
93
+ }>>(spec: T): { [K in keyof T]: T[K] extends ServiceMethod<infer P, infer R> ? ServiceMethod<P, R> : T[K] extends {
56
94
  handler: ServiceMethod<infer P, infer R_1>;
57
- } ? ServiceMethodDescriptor<P, R_1> : never; };
95
+ } ? ServiceMethod<P, R_1> : never; };
58
96
  declare const defineServiceRegistry: <R extends ServiceRegistry>(registry: R) => R;
59
97
 
60
98
  type RequestContext<L extends Logs = Logs> = {
@@ -73,6 +111,7 @@ type RouteCSPConfig = {
73
111
  req: FastifyRequest;
74
112
  }) => CSPDirectives);
75
113
  generateCSP?: (directives: CSPDirectives, nonce: string, req: FastifyRequest) => string;
114
+ reportOnly?: boolean;
76
115
  };
77
116
  type BaseMiddleware = {
78
117
  auth?: {
@@ -82,12 +121,7 @@ type BaseMiddleware = {
82
121
  };
83
122
  csp?: RouteCSPConfig | false;
84
123
  };
85
- type ServiceCall = {
86
- serviceName: string;
87
- serviceMethod: string;
88
- args?: Record<string, unknown>;
89
- };
90
- type DataResult = Record<string, unknown> | ServiceCall;
124
+ type DataResult = Record<string, unknown> | ServiceDescriptor;
91
125
  type DataHandler<Params extends PathToRegExpParams, L extends Logs = Logs> = (params: Params, ctx: RequestContext<L> & {
92
126
  [key: string]: unknown;
93
127
  }) => Promise<DataResult>;
@@ -172,4 +206,4 @@ type TaujsConfig = {
172
206
 
173
207
  declare function defineConfig<T extends TaujsConfig>(config: T): T;
174
208
 
175
- export { type AppConfig as A, type BaseLogger as B, type DebugConfig as D, type InitialRouteParams as I, type ServiceRegistry as S, type TaujsConfig as T, type SecurityConfig as a, type AppRoute as b, defineServiceRegistry as c, defineConfig as d, defineService as e };
209
+ export { type AppConfig as A, type BaseLogger as B, type DebugConfig as D, type InitialRouteParams as I, type ServiceRegistry as S, type TaujsConfig as T, type SecurityConfig as a, type AppRoute as b, createLogger as c, defineConfig as d, defineServiceRegistry as e, defineService as f, type ServiceContext as g };
package/dist/Config.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import 'fastify';
2
2
  import 'vite';
3
- export { A as AppConfig, b as AppRoute, a as SecurityConfig, T as TaujsConfig, d as defineConfig, e as defineService, c as defineServiceRegistry } from './Config-LCDjtT9m.js';
3
+ export { A as AppConfig, b as AppRoute, a as SecurityConfig, g as ServiceContext, T as TaujsConfig, d as defineConfig, f as defineService, e as defineServiceRegistry } from './Config-BuUuMjmO.js';
package/dist/Config.js CHANGED
@@ -1,19 +1,26 @@
1
1
  // src/utils/DataServices.ts
2
- var defineService = (spec) => {
2
+ import { performance } from "perf_hooks";
3
+ var runSchema = (schema, input) => {
4
+ if (!schema) return input;
5
+ return typeof schema.parse === "function" ? schema.parse(input) : schema(input);
6
+ };
7
+ function defineService(spec) {
3
8
  const out = {};
4
- for (const [k, v] of Object.entries(spec)) {
9
+ for (const [name, v] of Object.entries(spec)) {
5
10
  if (typeof v === "function") {
6
- out[k] = { handler: v };
11
+ out[name] = v;
7
12
  } else {
8
- out[k] = {
9
- handler: v.handler,
10
- parsers: v.parsers ?? (v.params || v.result ? { params: v.params, result: v.result } : void 0)
13
+ const { handler, params: paramsSchema, result: resultSchema } = v;
14
+ out[name] = async (params, ctx) => {
15
+ const p = runSchema(paramsSchema, params);
16
+ const r = await handler(p, ctx);
17
+ return runSchema(resultSchema, r);
11
18
  };
12
19
  }
13
20
  }
14
- return out;
15
- };
16
- var defineServiceRegistry = (registry) => registry;
21
+ return Object.freeze(out);
22
+ }
23
+ var defineServiceRegistry = (registry) => Object.freeze(Object.fromEntries(Object.entries(registry).map(([k, v]) => [k, Object.freeze(v)])));
17
24
 
18
25
  // src/Config.ts
19
26
  function defineConfig(config) {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FastifyPluginCallback, FastifyPluginAsync, FastifyInstance } from 'fastify';
2
- import { T as TaujsConfig, S as ServiceRegistry, D as DebugConfig, B as BaseLogger, A as AppConfig } from './Config-LCDjtT9m.js';
3
- export { I as InitialRouteParams } from './Config-LCDjtT9m.js';
2
+ import { T as TaujsConfig, S as ServiceRegistry, D as DebugConfig, B as BaseLogger, A as AppConfig } from './Config-BuUuMjmO.js';
3
+ export { I as InitialRouteParams, c as createLogger } from './Config-BuUuMjmO.js';
4
4
  import 'vite';
5
5
 
6
6
  type StaticMountEntry = {
@@ -51,4 +51,13 @@ declare function taujsBuild({ config, projectRoot, clientBaseDir, isSSRBuild, }:
51
51
  isSSRBuild?: boolean;
52
52
  }): Promise<void>;
53
53
 
54
- export { createServer, taujsBuild };
54
+ interface MessageMetaLogger {
55
+ debug?: (message?: string, meta?: Record<string, unknown>) => unknown;
56
+ info?: (message?: string, meta?: Record<string, unknown>) => unknown;
57
+ warn?: (message?: string, meta?: Record<string, unknown>) => unknown;
58
+ error?: (message?: string, meta?: Record<string, unknown>) => unknown;
59
+ child?: (bindings: Record<string, unknown>) => MessageMetaLogger | undefined;
60
+ }
61
+ declare function winstonAdapter(winston: MessageMetaLogger): BaseLogger;
62
+
63
+ export { createServer, taujsBuild, winstonAdapter };
package/dist/index.js CHANGED
@@ -190,7 +190,7 @@ var require_plugin = __commonJS({
190
190
  // src/CreateServer.ts
191
191
  var import_picocolors4 = __toESM(require_picocolors(), 1);
192
192
  import path5 from "path";
193
- import { performance as performance2 } from "perf_hooks";
193
+ import { performance as performance3 } from "perf_hooks";
194
194
  import fastifyStatic from "@fastify/static";
195
195
  import Fastify from "fastify";
196
196
 
@@ -348,9 +348,129 @@ var computeScore = (path7) => {
348
348
  return path7.split("/").filter(Boolean).reduce((score, segment) => score + (segment.startsWith(":") ? 1 : 10), 0);
349
349
  };
350
350
 
351
- // src/network/Network.ts
352
- var import_picocolors3 = __toESM(require_picocolors(), 1);
353
- import { networkInterfaces } from "os";
351
+ // src/logging/AppError.ts
352
+ var HTTP_STATUS = {
353
+ infra: 500,
354
+ upstream: 502,
355
+ domain: 404,
356
+ validation: 400,
357
+ auth: 403,
358
+ canceled: 499,
359
+ // Client Closed Request (nginx convention)
360
+ timeout: 504
361
+ };
362
+ var AppError = class _AppError extends Error {
363
+ kind;
364
+ httpStatus;
365
+ details;
366
+ safeMessage;
367
+ code;
368
+ constructor(message, kind, options = {}) {
369
+ super(message);
370
+ this.name = "AppError";
371
+ Object.setPrototypeOf(this, new.target.prototype);
372
+ if (options.cause !== void 0) {
373
+ Object.defineProperty(this, "cause", {
374
+ value: options.cause,
375
+ enumerable: false,
376
+ writable: false,
377
+ configurable: true
378
+ });
379
+ }
380
+ this.kind = kind;
381
+ this.httpStatus = options.httpStatus ?? HTTP_STATUS[kind];
382
+ this.details = options.details;
383
+ this.safeMessage = options.safeMessage ?? this.getSafeMessage(kind, message);
384
+ this.code = options.code;
385
+ if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
386
+ }
387
+ getSafeMessage(kind, message) {
388
+ return kind === "domain" || kind === "validation" || kind === "auth" ? message : "Internal Server Error";
389
+ }
390
+ serialiseValue(value, seen = /* @__PURE__ */ new WeakSet()) {
391
+ if (value === null || value === void 0) return value;
392
+ if (typeof value !== "object") return value;
393
+ if (seen.has(value)) return "[circular]";
394
+ seen.add(value);
395
+ if (value instanceof Error) {
396
+ return {
397
+ name: value.name,
398
+ message: value.message,
399
+ stack: value.stack,
400
+ ...value instanceof _AppError && {
401
+ kind: value.kind,
402
+ httpStatus: value.httpStatus,
403
+ code: value.code
404
+ }
405
+ };
406
+ }
407
+ if (Array.isArray(value)) return value.map((item) => this.serialiseValue(item, seen));
408
+ const result = {};
409
+ for (const [key, val] of Object.entries(value)) {
410
+ result[key] = this.serialiseValue(val, seen);
411
+ }
412
+ return result;
413
+ }
414
+ toJSON() {
415
+ return {
416
+ name: this.name,
417
+ kind: this.kind,
418
+ message: this.message,
419
+ safeMessage: this.safeMessage,
420
+ httpStatus: this.httpStatus,
421
+ ...this.code && { code: this.code },
422
+ details: this.serialiseValue(this.details),
423
+ stack: this.stack,
424
+ ...this.cause && {
425
+ cause: this.serialiseValue(this.cause)
426
+ }
427
+ };
428
+ }
429
+ static notFound(message, details, code) {
430
+ return new _AppError(message, "domain", { httpStatus: 404, details, code });
431
+ }
432
+ static forbidden(message, details, code) {
433
+ return new _AppError(message, "auth", { httpStatus: 403, details, code });
434
+ }
435
+ static badRequest(message, details, code) {
436
+ return new _AppError(message, "validation", { httpStatus: 400, details, code });
437
+ }
438
+ static unprocessable(message, details, code) {
439
+ return new _AppError(message, "validation", { httpStatus: 422, details, code });
440
+ }
441
+ static timeout(message, details, code) {
442
+ return new _AppError(message, "timeout", { details, code });
443
+ }
444
+ static canceled(message, details, code) {
445
+ return new _AppError(message, "canceled", { details, code });
446
+ }
447
+ static internal(message, cause, details, code) {
448
+ return new _AppError(message, "infra", { cause, details, code });
449
+ }
450
+ static upstream(message, cause, details, code) {
451
+ return new _AppError(message, "upstream", { cause, details, code });
452
+ }
453
+ static serviceUnavailable(message, cause, details, code) {
454
+ return new _AppError(message, "infra", { httpStatus: 503, cause, details, code });
455
+ }
456
+ static from(err, fallback = "Internal error") {
457
+ return err instanceof _AppError ? err : _AppError.internal(err?.message ?? fallback, err);
458
+ }
459
+ };
460
+ function normaliseError(e) {
461
+ if (e instanceof Error) return { name: e.name, message: e.message, stack: e.stack };
462
+ const hasMessageProp = e != null && typeof e.message !== "undefined";
463
+ const msg = hasMessageProp ? String(e.message) : String(e);
464
+ return { name: "Error", message: msg };
465
+ }
466
+ function toReason(e) {
467
+ if (e instanceof Error) return e;
468
+ if (e === null) return new Error("null");
469
+ if (typeof e === "undefined") return new Error("Unknown render error");
470
+ const maybeMsg = e?.message;
471
+ if (typeof maybeMsg !== "undefined") return new Error(String(maybeMsg));
472
+ return new Error(String(e));
473
+ }
354
474
 
355
475
  // src/logging/Logger.ts
356
476
  var import_picocolors2 = __toESM(require_picocolors(), 1);
@@ -554,7 +674,54 @@ function createLogger(opts) {
554
674
  return logger;
555
675
  }
556
676
 
677
+ // src/network/CLI.ts
678
+ function readFlag(argv, keys, bareValue) {
679
+ const end = argv.indexOf("--");
680
+ const limit = end === -1 ? argv.length : end;
681
+ for (let i = 0; i < limit; i++) {
682
+ const arg = argv[i];
683
+ for (const key of keys) {
684
+ if (arg === key) {
685
+ const next = argv[i + 1];
686
+ if (!next || next.startsWith("-")) return bareValue;
687
+ return next.trim();
688
+ }
689
+ const pref = `${key}=`;
690
+ if (arg && arg.startsWith(pref)) {
691
+ const v = arg.slice(pref.length).trim();
692
+ return v || bareValue;
693
+ }
694
+ }
695
+ }
696
+ return void 0;
697
+ }
698
+ function resolveNet(input) {
699
+ const env = process.env;
700
+ const argv = process.argv;
701
+ let host = "localhost";
702
+ let port = 5173;
703
+ let hmrPort = 5174;
704
+ if (input?.host) host = input.host;
705
+ if (Number.isFinite(input?.port)) port = Number(input.port);
706
+ if (Number.isFinite(input?.hmrPort)) hmrPort = Number(input.hmrPort);
707
+ if (env.HOST?.trim()) host = env.HOST.trim();
708
+ else if (env.FASTIFY_ADDRESS?.trim()) host = env.FASTIFY_ADDRESS.trim();
709
+ if (env.PORT) port = Number(env.PORT) || port;
710
+ if (env.FASTIFY_PORT) port = Number(env.FASTIFY_PORT) || port;
711
+ if (env.HMR_PORT) hmrPort = Number(env.HMR_PORT) || hmrPort;
712
+ const cliHost = readFlag(argv, ["--host", "--hostname", "-H"], "0.0.0.0");
713
+ const cliPort = readFlag(argv, ["--port", "-p"]);
714
+ const cliHMR = readFlag(argv, ["--hmr-port"]);
715
+ if (cliHost) host = cliHost;
716
+ if (cliPort) port = Number(cliPort) || port;
717
+ if (cliHMR) hmrPort = Number(cliHMR) || hmrPort;
718
+ if (host === "true" || host === "") host = "0.0.0.0";
719
+ return { host, port, hmrPort };
720
+ }
721
+
557
722
  // src/network/Network.ts
723
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
724
+ import { networkInterfaces } from "os";
558
725
  var isPrivateIPv4 = (addr) => {
559
726
  if (!/^\d+\.\d+\.\d+\.\d+$/.test(addr)) return false;
560
727
  const [a, b, _c, _d] = addr.split(".").map(Number);
@@ -606,51 +773,6 @@ var bannerPlugin = async (fastify, options) => {
606
773
  });
607
774
  };
608
775
 
609
- // src/network/CLI.ts
610
- function readFlag(argv, keys, bareValue) {
611
- const end = argv.indexOf("--");
612
- const limit = end === -1 ? argv.length : end;
613
- for (let i = 0; i < limit; i++) {
614
- const arg = argv[i];
615
- for (const key of keys) {
616
- if (arg === key) {
617
- const next = argv[i + 1];
618
- if (!next || next.startsWith("-")) return bareValue;
619
- return next.trim();
620
- }
621
- const pref = `${key}=`;
622
- if (arg && arg.startsWith(pref)) {
623
- const v = arg.slice(pref.length).trim();
624
- return v || bareValue;
625
- }
626
- }
627
- }
628
- return void 0;
629
- }
630
- function resolveNet(input) {
631
- const env = process.env;
632
- const argv = process.argv;
633
- let host = "localhost";
634
- let port = 5173;
635
- let hmrPort = 5174;
636
- if (input?.host) host = input.host;
637
- if (Number.isFinite(input?.port)) port = Number(input.port);
638
- if (Number.isFinite(input?.hmrPort)) hmrPort = Number(input.hmrPort);
639
- if (env.HOST?.trim()) host = env.HOST.trim();
640
- else if (env.FASTIFY_ADDRESS?.trim()) host = env.FASTIFY_ADDRESS.trim();
641
- if (env.PORT) port = Number(env.PORT) || port;
642
- if (env.FASTIFY_PORT) port = Number(env.FASTIFY_PORT) || port;
643
- if (env.HMR_PORT) hmrPort = Number(env.HMR_PORT) || hmrPort;
644
- const cliHost = readFlag(argv, ["--host", "--hostname", "-H"], "0.0.0.0");
645
- const cliPort = readFlag(argv, ["--port", "-p"]);
646
- const cliHMR = readFlag(argv, ["--hmr-port"]);
647
- if (cliHost) host = cliHost;
648
- if (cliPort) port = Number(cliPort) || port;
649
- if (cliHMR) hmrPort = Number(cliHMR) || hmrPort;
650
- if (host === "true" || host === "") host = "0.0.0.0";
651
- return { host, port, hmrPort };
652
- }
653
-
654
776
  // src/security/VerifyMiddleware.ts
655
777
  var isAuthRequired = (route) => Boolean(route.attr?.middleware?.auth);
656
778
  var hasAuthenticate = (app) => typeof app.authenticate === "function";
@@ -718,130 +840,6 @@ var verifyContracts = (app, routes, contracts, security) => {
718
840
  // src/SSRServer.ts
719
841
  var import_fastify_plugin3 = __toESM(require_plugin(), 1);
720
842
 
721
- // src/logging/AppError.ts
722
- var HTTP_STATUS = {
723
- infra: 500,
724
- upstream: 502,
725
- domain: 404,
726
- validation: 400,
727
- auth: 403,
728
- canceled: 499,
729
- // Client Closed Request (nginx convention)
730
- timeout: 504
731
- };
732
- var AppError = class _AppError extends Error {
733
- kind;
734
- httpStatus;
735
- details;
736
- safeMessage;
737
- code;
738
- constructor(message, kind, options = {}) {
739
- super(message);
740
- this.name = "AppError";
741
- Object.setPrototypeOf(this, new.target.prototype);
742
- if (options.cause !== void 0) {
743
- Object.defineProperty(this, "cause", {
744
- value: options.cause,
745
- enumerable: false,
746
- writable: false,
747
- configurable: true
748
- });
749
- }
750
- this.kind = kind;
751
- this.httpStatus = options.httpStatus ?? HTTP_STATUS[kind];
752
- this.details = options.details;
753
- this.safeMessage = options.safeMessage ?? this.getSafeMessage(kind, message);
754
- this.code = options.code;
755
- if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
756
- }
757
- getSafeMessage(kind, message) {
758
- return kind === "domain" || kind === "validation" || kind === "auth" ? message : "Internal Server Error";
759
- }
760
- serialiseValue(value, seen = /* @__PURE__ */ new WeakSet()) {
761
- if (value === null || value === void 0) return value;
762
- if (typeof value !== "object") return value;
763
- if (seen.has(value)) return "[circular]";
764
- seen.add(value);
765
- if (value instanceof Error) {
766
- return {
767
- name: value.name,
768
- message: value.message,
769
- stack: value.stack,
770
- ...value instanceof _AppError && {
771
- kind: value.kind,
772
- httpStatus: value.httpStatus,
773
- code: value.code
774
- }
775
- };
776
- }
777
- if (Array.isArray(value)) return value.map((item) => this.serialiseValue(item, seen));
778
- const result = {};
779
- for (const [key, val] of Object.entries(value)) {
780
- result[key] = this.serialiseValue(val, seen);
781
- }
782
- return result;
783
- }
784
- toJSON() {
785
- return {
786
- name: this.name,
787
- kind: this.kind,
788
- message: this.message,
789
- safeMessage: this.safeMessage,
790
- httpStatus: this.httpStatus,
791
- ...this.code && { code: this.code },
792
- details: this.serialiseValue(this.details),
793
- stack: this.stack,
794
- ...this.cause && {
795
- cause: this.serialiseValue(this.cause)
796
- }
797
- };
798
- }
799
- static notFound(message, details, code) {
800
- return new _AppError(message, "domain", { httpStatus: 404, details, code });
801
- }
802
- static forbidden(message, details, code) {
803
- return new _AppError(message, "auth", { httpStatus: 403, details, code });
804
- }
805
- static badRequest(message, details, code) {
806
- return new _AppError(message, "validation", { httpStatus: 400, details, code });
807
- }
808
- static unprocessable(message, details, code) {
809
- return new _AppError(message, "validation", { httpStatus: 422, details, code });
810
- }
811
- static timeout(message, details, code) {
812
- return new _AppError(message, "timeout", { details, code });
813
- }
814
- static canceled(message, details, code) {
815
- return new _AppError(message, "canceled", { details, code });
816
- }
817
- static internal(message, cause, details, code) {
818
- return new _AppError(message, "infra", { cause, details, code });
819
- }
820
- static upstream(message, cause, details, code) {
821
- return new _AppError(message, "upstream", { cause, details, code });
822
- }
823
- static serviceUnavailable(message, cause, details, code) {
824
- return new _AppError(message, "infra", { httpStatus: 503, cause, details, code });
825
- }
826
- static from(err, fallback = "Internal error") {
827
- return err instanceof _AppError ? err : _AppError.internal(err?.message ?? fallback, err);
828
- }
829
- };
830
- function normaliseError(e) {
831
- if (e instanceof Error) return { name: e.name, message: e.message, stack: e.stack };
832
- const hasMessageProp = e != null && typeof e.message !== "undefined";
833
- const msg = hasMessageProp ? String(e.message) : String(e);
834
- return { name: "Error", message: msg };
835
- }
836
- function toReason(e) {
837
- if (e instanceof Error) return e;
838
- if (e === null) return new Error("null");
839
- if (typeof e === "undefined") return new Error("Unknown render error");
840
- const maybeMsg = e?.message;
841
- if (typeof maybeMsg !== "undefined") return new Error(String(maybeMsg));
842
- return new Error(String(e));
843
- }
844
-
845
843
  // src/logging/utils/index.ts
846
844
  var httpStatusFrom = (err, fallback = 500) => err instanceof AppError ? err.httpStatus : fallback;
847
845
  var toHttp = (err) => {
@@ -880,39 +878,47 @@ var statusText = (status) => {
880
878
  import { match } from "path-to-regexp";
881
879
 
882
880
  // src/utils/DataServices.ts
881
+ import { performance as performance2 } from "perf_hooks";
883
882
  async function callServiceMethod(registry, serviceName, methodName, params, ctx) {
884
883
  if (ctx.signal?.aborted) throw AppError.timeout("Request canceled");
885
884
  const service = registry[serviceName];
886
885
  if (!service) throw AppError.notFound(`Unknown service: ${serviceName}`);
887
- const desc = service[methodName];
888
- if (!desc) throw AppError.notFound(`Unknown method: ${serviceName}.${methodName}`);
889
- const logger = ctx.logger?.child({
886
+ const method = service[methodName];
887
+ if (!method) throw AppError.notFound(`Unknown method: ${serviceName}.${methodName}`);
888
+ const logger = ctx.logger?.child?.({
890
889
  component: "service-call",
891
890
  service: serviceName,
892
891
  method: methodName,
893
892
  traceId: ctx.traceId
894
893
  });
894
+ const t0 = performance2.now();
895
895
  try {
896
- const p = desc.parsers?.params ? desc.parsers.params(params) : params;
897
- const data = await desc.handler(p, ctx);
898
- const out = desc.parsers?.result ? desc.parsers.result(data) : data;
899
- if (typeof out !== "object" || out === null) throw AppError.internal(`Non-object result from ${serviceName}.${methodName}`);
900
- return out;
896
+ const result = await method(params ?? {}, ctx);
897
+ if (typeof result !== "object" || result === null) {
898
+ throw AppError.internal(`Non-object result from ${serviceName}.${methodName}`);
899
+ }
900
+ logger?.debug?.({ ms: +(performance2.now() - t0).toFixed(1) }, "Service method ok");
901
+ return result;
901
902
  } catch (err) {
902
- logger?.error(
903
+ logger?.error?.(
903
904
  {
904
905
  params,
905
- error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err)
906
+ error: err instanceof Error ? { name: err.name, message: err.message, stack: err.stack } : String(err),
907
+ ms: +(performance2.now() - t0).toFixed(1)
906
908
  },
907
909
  "Service method failed"
908
910
  );
909
- throw err;
911
+ throw err instanceof AppError ? err : err instanceof Error ? AppError.internal(err.message, { cause: err }) : AppError.internal("Unknown error", { details: { err } });
910
912
  }
911
913
  }
912
914
  var isServiceDescriptor = (obj) => {
913
- if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
914
- const maybe = obj;
915
- return typeof maybe.serviceName === "string" && typeof maybe.serviceMethod === "string";
915
+ if (!obj || typeof obj !== "object" || Array.isArray(obj)) return false;
916
+ const o = obj;
917
+ if (typeof o.serviceName !== "string" || typeof o.serviceMethod !== "string") return false;
918
+ if ("args" in o) {
919
+ if (o.args === null || typeof o.args !== "object" || Array.isArray(o.args)) return false;
920
+ }
921
+ return true;
916
922
  };
917
923
 
918
924
  // src/utils/DataRoutes.ts
@@ -1019,6 +1025,16 @@ var createAuthHook = (routeMatchers, logger) => {
1019
1025
  if (!match2) return;
1020
1026
  const { route } = match2;
1021
1027
  const authConfig = route.attr?.middleware?.auth;
1028
+ req.routeMeta = {
1029
+ path: route.path,
1030
+ appId: route.appId,
1031
+ attr: {
1032
+ middleware: {
1033
+ auth: route.attr?.middleware?.auth
1034
+ },
1035
+ render: route.attr?.render
1036
+ }
1037
+ };
1022
1038
  if (!authConfig) {
1023
1039
  logger.debug("auth", { method: req.method, url: req.url }, "(none)");
1024
1040
  return;
@@ -1103,42 +1119,27 @@ var cspPlugin = (0, import_fastify_plugin.default)(
1103
1119
  fastify.addHook("onRequest", (req, reply, done) => {
1104
1120
  const nonce = generateNonce();
1105
1121
  req.cspNonce = nonce;
1122
+ const headerNameFor = (routeCSP2) => routeCSP2 && typeof routeCSP2 === "object" && routeCSP2.reportOnly || opts.reporting?.reportOnly ? "Content-Security-Policy-Report-Only" : "Content-Security-Policy";
1123
+ let routeCSP;
1106
1124
  try {
1107
1125
  const routeMatch = findMatchingRoute(matchers, req.url);
1108
- const routeCSP = routeMatch?.route.attr?.middleware?.csp;
1126
+ routeCSP = routeMatch?.route.attr?.middleware?.csp;
1109
1127
  if (routeCSP === false) {
1110
1128
  done();
1111
1129
  return;
1112
1130
  }
1113
1131
  let finalDirectives = globalDirectives;
1114
- if (routeCSP && typeof routeCSP === "object") {
1115
- if (!routeCSP.disabled) {
1116
- let routeDirectives;
1117
- if (typeof routeCSP.directives === "function") {
1118
- const params = routeMatch?.params || {};
1119
- routeDirectives = routeCSP.directives({
1120
- url: req.url,
1121
- params,
1122
- headers: req.headers,
1123
- req
1124
- });
1125
- } else {
1126
- routeDirectives = routeCSP.directives || {};
1127
- }
1128
- if (routeCSP.mode === "replace") {
1129
- finalDirectives = routeDirectives;
1130
- } else {
1131
- finalDirectives = mergeDirectives(globalDirectives, routeDirectives);
1132
- }
1133
- }
1134
- }
1135
- let cspHeader;
1136
- if (routeCSP?.generateCSP) {
1137
- cspHeader = routeCSP.generateCSP(finalDirectives, nonce, req);
1138
- } else {
1139
- cspHeader = generateCSP(finalDirectives, nonce, req);
1132
+ if (routeCSP && typeof routeCSP === "object" && !routeCSP.disabled) {
1133
+ const routeDirectives = typeof routeCSP.directives === "function" ? routeCSP.directives({
1134
+ url: req.url,
1135
+ params: routeMatch?.params || {},
1136
+ headers: req.headers,
1137
+ req
1138
+ }) : routeCSP.directives ?? {};
1139
+ finalDirectives = routeCSP.mode === "replace" ? routeDirectives : mergeDirectives(globalDirectives, routeDirectives);
1140
1140
  }
1141
- reply.header("Content-Security-Policy", cspHeader);
1141
+ const cspHeader = routeCSP?.generateCSP ? routeCSP.generateCSP(finalDirectives, nonce, req) : generateCSP(finalDirectives, nonce, req);
1142
+ reply.header(headerNameFor(routeCSP), cspHeader);
1142
1143
  } catch (error) {
1143
1144
  logger.error(
1144
1145
  {
@@ -1148,7 +1149,7 @@ var cspPlugin = (0, import_fastify_plugin.default)(
1148
1149
  "CSP plugin error"
1149
1150
  );
1150
1151
  const fallbackHeader = generateCSP(globalDirectives, nonce, req);
1151
- reply.header("Content-Security-Policy", fallbackHeader);
1152
+ reply.header(headerNameFor(routeCSP), fallbackHeader);
1152
1153
  }
1153
1154
  done();
1154
1155
  });
@@ -2024,20 +2025,21 @@ var SSRServer = (0, import_fastify_plugin3.default)(
2024
2025
 
2025
2026
  // src/CreateServer.ts
2026
2027
  var createServer = async (opts) => {
2027
- const t0 = performance2.now();
2028
+ const t0 = performance3.now();
2028
2029
  const clientRoot = opts.clientRoot ?? path5.resolve(process.cwd(), "client");
2029
2030
  const app = opts.fastify ?? Fastify({ logger: false });
2030
- const net = resolveNet(opts.config.server);
2031
- await app.register(bannerPlugin, {
2032
- debug: opts.debug,
2033
- hmr: { host: net.host, port: net.hmrPort }
2034
- });
2031
+ const fastifyLogger = app.log && app.log.level !== "silent" ? app.log : void 0;
2035
2032
  const logger = createLogger({
2036
2033
  debug: opts.debug,
2037
- custom: opts.logger,
2034
+ custom: opts.logger ?? fastifyLogger,
2038
2035
  minLevel: process.env.NODE_ENV === "production" ? "info" : "debug",
2039
2036
  includeContext: true
2040
2037
  });
2038
+ const net = resolveNet(opts.config.server);
2039
+ await app.register(bannerPlugin, {
2040
+ debug: opts.debug,
2041
+ hmr: { host: net.host, port: net.hmrPort }
2042
+ });
2041
2043
  const configs = extractBuildConfigs(opts.config);
2042
2044
  const { routes, apps, totalRoutes, durationMs, warnings } = extractRoutes(opts.config);
2043
2045
  const { security, durationMs: securityDuration, hasExplicitCSP } = extractSecurity(opts.config);
@@ -2084,7 +2086,7 @@ var createServer = async (opts) => {
2084
2086
  "Failed to register SSRServer"
2085
2087
  );
2086
2088
  }
2087
- const t1 = performance2.now();
2089
+ const t1 = performance3.now();
2088
2090
  console.log(`
2089
2091
  ${import_picocolors4.default.bgGreen(import_picocolors4.default.black(` ${CONTENT.TAG} `))} configured in ${(t1 - t0).toFixed(0)}ms
2090
2092
  `);
@@ -2174,7 +2176,24 @@ async function taujsBuild({
2174
2176
  }
2175
2177
  }
2176
2178
  }
2179
+
2180
+ // src/logging/Adapters.ts
2181
+ var cleanMeta = (m) => m && Object.keys(m).length === 0 ? void 0 : m;
2182
+ function messageMetaAdapter(sink) {
2183
+ return {
2184
+ debug: (meta, message) => sink.debug?.(message, cleanMeta(meta)),
2185
+ info: (meta, message) => sink.info?.(message, cleanMeta(meta)),
2186
+ warn: (meta, message) => sink.warn?.(message, cleanMeta(meta)),
2187
+ error: (meta, message) => sink.error?.(message, cleanMeta(meta)),
2188
+ child: (ctx) => messageMetaAdapter(sink.child?.(ctx) ?? sink)
2189
+ };
2190
+ }
2191
+ function winstonAdapter(winston) {
2192
+ return messageMetaAdapter(winston);
2193
+ }
2177
2194
  export {
2195
+ createLogger,
2178
2196
  createServer,
2179
- taujsBuild
2197
+ taujsBuild,
2198
+ winstonAdapter
2180
2199
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taujs/server",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "τjs [ taujs ]",
5
5
  "author": "John Smith | Aoede <taujs@aoede.uk.net> (https://www.aoede.uk.net)",
6
6
  "license": "MIT",