@dunx/http 2.1.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/health/registry.d.ts +1 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +561 -47
- package/dist/index.js.map +19 -11
- package/dist/server/application.d.ts +31 -0
- package/dist/server/errors.d.ts +15 -1
- package/dist/server/factory.d.ts +1 -0
- package/dist/server/request-id.d.ts +27 -0
- package/dist/server/request-logging.d.ts +0 -1
- package/dist/server/routes.d.ts +20 -0
- package/dist/static/module.d.ts +36 -2
- package/dist/throttle/decorators.d.ts +22 -0
- package/dist/throttle/guard.d.ts +31 -0
- package/dist/throttle/module.d.ts +35 -0
- package/dist/throttle/options.d.ts +52 -0
- package/dist/throttle/store.d.ts +71 -0
- package/dist/ws/adapter.d.ts +7 -1
- package/dist/ws/logging.d.ts +69 -0
- package/dist/ws/middleware.d.ts +95 -0
- package/dist/ws/socket.d.ts +18 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -340,13 +340,15 @@ import { AppError as AppError3, ConsoleLogger } from "@dunx/core";
|
|
|
340
340
|
class HttpError extends AppError3 {
|
|
341
341
|
status;
|
|
342
342
|
name = "HttpError";
|
|
343
|
+
headers;
|
|
343
344
|
constructor(status, message, options) {
|
|
344
345
|
super(message, options);
|
|
345
346
|
this.status = status;
|
|
347
|
+
this.headers = options?.headers;
|
|
346
348
|
}
|
|
347
349
|
}
|
|
348
350
|
Object.defineProperty(HttpError, Symbol.for("dunx.deps"), {
|
|
349
|
-
value: () => [{ unresolved: "readonly status: number" }, { unresolved: "message: string" },
|
|
351
|
+
value: () => [{ unresolved: "readonly status: number" }, { unresolved: "message: string" }, { unresolved: "options?: HttpErrorOptions" }]
|
|
350
352
|
});
|
|
351
353
|
|
|
352
354
|
class ValidationError extends HttpError {
|
|
@@ -369,10 +371,16 @@ var isErrorFilter = (handler) => typeof handler === "function" && typeof handler
|
|
|
369
371
|
var toErrorMapper = (handler, resolve) => isErrorFilter(handler) ? (error, req) => resolve(handler).catch(error, req) : handler;
|
|
370
372
|
var errorMapper = (logger) => (error) => {
|
|
371
373
|
if (error instanceof ValidationError) {
|
|
372
|
-
return Response.json({ error: error.message, status: error.status, issues: error.issues }, {
|
|
374
|
+
return Response.json({ error: error.message, status: error.status, issues: error.issues }, {
|
|
375
|
+
status: error.status,
|
|
376
|
+
...error.headers && { headers: error.headers }
|
|
377
|
+
});
|
|
373
378
|
}
|
|
374
379
|
if (error instanceof HttpError) {
|
|
375
|
-
return Response.json({ error: error.message, status: error.status }, {
|
|
380
|
+
return Response.json({ error: error.message, status: error.status }, {
|
|
381
|
+
status: error.status,
|
|
382
|
+
...error.headers && { headers: error.headers }
|
|
383
|
+
});
|
|
376
384
|
}
|
|
377
385
|
logger.error("Unhandled error", error);
|
|
378
386
|
return Response.json({
|
|
@@ -386,10 +394,10 @@ import {
|
|
|
386
394
|
collectModules as collectModules2,
|
|
387
395
|
AppError as AppError8,
|
|
388
396
|
AppFactory,
|
|
389
|
-
Logger as
|
|
397
|
+
Logger as Logger4,
|
|
390
398
|
provide,
|
|
391
399
|
readControllers as readControllers2,
|
|
392
|
-
RequestContext as
|
|
400
|
+
RequestContext as RequestContext3
|
|
393
401
|
} from "@dunx/core";
|
|
394
402
|
|
|
395
403
|
// src/ws/envelope.ts
|
|
@@ -409,6 +417,29 @@ var decode = (message) => {
|
|
|
409
417
|
return typeof event === "string" ? { event, data } : undefined;
|
|
410
418
|
};
|
|
411
419
|
|
|
420
|
+
// src/ws/middleware.ts
|
|
421
|
+
var composeSocket = (middleware, ctx) => middleware.reduceRight((next, current) => (frame, run) => current.handle(frame, ctx, () => next(frame, run)), (_frame, run) => run());
|
|
422
|
+
var observe = (next, done) => {
|
|
423
|
+
let result;
|
|
424
|
+
try {
|
|
425
|
+
result = next();
|
|
426
|
+
} catch (error) {
|
|
427
|
+
done(error, undefined);
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
if (result instanceof Promise) {
|
|
431
|
+
return result.then((value) => {
|
|
432
|
+
done(undefined, value);
|
|
433
|
+
return value;
|
|
434
|
+
}, (error) => {
|
|
435
|
+
done(error, undefined);
|
|
436
|
+
throw error;
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
done(undefined, result);
|
|
440
|
+
return result;
|
|
441
|
+
};
|
|
442
|
+
|
|
412
443
|
// src/ws/runtime.ts
|
|
413
444
|
import { AppError as AppError4 } from "@dunx/core";
|
|
414
445
|
var slotOf = (handler) => handler.kind === HandlerKind.MESSAGE && handler.event !== undefined ? `message ${JSON.stringify(handler.event)}` : handler.kind;
|
|
@@ -463,9 +494,14 @@ var someHandler = (gateways, pick) => {
|
|
|
463
494
|
|
|
464
495
|
// src/ws/adapter.ts
|
|
465
496
|
var RUNTIME = Symbol.for("dunx.ws.runtime");
|
|
497
|
+
var UNCLAIMED = Symbol.for("dunx.ws.unclaimed");
|
|
466
498
|
var defaultOnError = (error, socket) => {
|
|
467
499
|
console.error(`[dunx/http] ${socket.data.path} handler failed:`, error);
|
|
468
500
|
};
|
|
501
|
+
var reportedByMiddleware = () => {
|
|
502
|
+
return;
|
|
503
|
+
};
|
|
504
|
+
var unreported = (middleware) => "Socket middleware is installed and none of it sets reportsErrors, so a " + "throwing gateway handler is reported nowhere: the console fallback is off " + "whenever middleware wraps the handler. Set reportsErrors on the one that " + "records a failure, or pass websocket.onError. Installed: " + `${middleware.map((entry) => entry.constructor.name).join(", ")}.`;
|
|
469
505
|
var runtimeOf = (socket) => socket.data[RUNTIME];
|
|
470
506
|
var isBinary = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
471
507
|
var replyRaw = (socket, value) => {
|
|
@@ -489,10 +525,67 @@ var settle = (result, socket, onError, then) => {
|
|
|
489
525
|
if (then)
|
|
490
526
|
then(result);
|
|
491
527
|
};
|
|
492
|
-
var
|
|
528
|
+
var framing = (kind) => {
|
|
529
|
+
if (kind === HandlerKind.CLOSE) {
|
|
530
|
+
return (args) => ({
|
|
531
|
+
socket: args[0],
|
|
532
|
+
data: { code: args[1], reason: args[2] }
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (kind === HandlerKind.OPEN || kind === HandlerKind.DRAIN) {
|
|
536
|
+
return (args) => ({ socket: args[0], data: undefined });
|
|
537
|
+
}
|
|
538
|
+
return (args) => ({ socket: args[1], data: args[0] });
|
|
539
|
+
};
|
|
540
|
+
var NOTHING = () => {
|
|
541
|
+
return;
|
|
542
|
+
};
|
|
543
|
+
var through = (gateway, middleware, kind, event, invoke) => {
|
|
544
|
+
const ctx = {
|
|
545
|
+
gateway: gateway.name,
|
|
546
|
+
path: gateway.path,
|
|
547
|
+
kind,
|
|
548
|
+
event
|
|
549
|
+
};
|
|
550
|
+
const dispatch = composeSocket(middleware, ctx);
|
|
551
|
+
const frameOf = framing(kind);
|
|
552
|
+
const run = invoke ?? NOTHING;
|
|
553
|
+
return (...args) => dispatch(frameOf(args), () => run(...args));
|
|
554
|
+
};
|
|
555
|
+
var withMiddleware = (gateway, middleware) => {
|
|
556
|
+
const wrap = (kind, event, invoke) => through(gateway, middleware, kind, event, invoke);
|
|
557
|
+
const optional = (kind, invoke) => invoke === undefined ? undefined : wrap(kind, undefined, invoke);
|
|
558
|
+
return {
|
|
559
|
+
...gateway,
|
|
560
|
+
open: wrap(HandlerKind.OPEN, undefined, gateway.open),
|
|
561
|
+
close: wrap(HandlerKind.CLOSE, undefined, gateway.close),
|
|
562
|
+
drain: optional(HandlerKind.DRAIN, gateway.drain),
|
|
563
|
+
ping: optional(HandlerKind.PING, gateway.ping),
|
|
564
|
+
pong: optional(HandlerKind.PONG, gateway.pong),
|
|
565
|
+
raw: optional(HandlerKind.MESSAGE, gateway.raw),
|
|
566
|
+
events: new Map([...gateway.events].map(([event, invoke]) => [
|
|
567
|
+
event,
|
|
568
|
+
wrap(HandlerKind.MESSAGE, event, invoke)
|
|
569
|
+
]))
|
|
570
|
+
};
|
|
571
|
+
};
|
|
572
|
+
var unclaimedDispatch = (gateway, middleware) => (frame, event) => composeSocket(middleware, {
|
|
573
|
+
gateway: gateway.name,
|
|
574
|
+
path: gateway.path,
|
|
575
|
+
kind: HandlerKind.MESSAGE,
|
|
576
|
+
event
|
|
577
|
+
})(frame, () => {
|
|
578
|
+
return;
|
|
579
|
+
});
|
|
580
|
+
var buildWebSocket = (discovered, options = {}, middleware = []) => {
|
|
493
581
|
const byPath = buildGateways(discovered);
|
|
494
|
-
const
|
|
495
|
-
|
|
582
|
+
const wrapped = middleware.length === 0 ? byPath : new Map([...byPath].map(([path, gateway]) => [
|
|
583
|
+
path,
|
|
584
|
+
withMiddleware(gateway, middleware)
|
|
585
|
+
]));
|
|
586
|
+
const gateways = [...wrapped.values()];
|
|
587
|
+
const onError = options.onError ?? (middleware.length === 0 ? defaultOnError : reportedByMiddleware);
|
|
588
|
+
const reports = options.onError !== undefined || middleware.some((entry) => entry.reportsErrors === true);
|
|
496
589
|
const { onError: _onError, ...socketOptions } = options;
|
|
497
590
|
const run = (invoke, args, ws, then) => {
|
|
498
591
|
try {
|
|
@@ -505,6 +598,7 @@ var buildWebSocket = (discovered, options = {}) => {
|
|
|
505
598
|
...socketOptions,
|
|
506
599
|
message(ws, message) {
|
|
507
600
|
const gateway = runtimeOf(ws);
|
|
601
|
+
let event;
|
|
508
602
|
if (gateway.events.size > 0) {
|
|
509
603
|
const envelope = decode(message);
|
|
510
604
|
const handler = envelope && gateway.events.get(envelope.event);
|
|
@@ -515,9 +609,19 @@ var buildWebSocket = (discovered, options = {}) => {
|
|
|
515
609
|
});
|
|
516
610
|
return;
|
|
517
611
|
}
|
|
612
|
+
event = envelope?.event;
|
|
518
613
|
}
|
|
519
614
|
if (gateway.raw) {
|
|
520
615
|
run(gateway.raw, [message, ws], ws, (value) => replyRaw(ws, value));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const unclaimed2 = ws.data[UNCLAIMED];
|
|
619
|
+
if (!unclaimed2)
|
|
620
|
+
return;
|
|
621
|
+
try {
|
|
622
|
+
settle(unclaimed2({ socket: ws, data: message }, event), ws, onError, undefined);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
onError(error, ws);
|
|
521
625
|
}
|
|
522
626
|
},
|
|
523
627
|
...someHandler(gateways, (g) => g.open) && {
|
|
@@ -556,8 +660,19 @@ var buildWebSocket = (discovered, options = {}) => {
|
|
|
556
660
|
}
|
|
557
661
|
}
|
|
558
662
|
};
|
|
663
|
+
const unclaimed = new Map(middleware.length === 0 ? [] : gateways.map((gateway) => [
|
|
664
|
+
gateway,
|
|
665
|
+
unclaimedDispatch(gateway, middleware)
|
|
666
|
+
]));
|
|
559
667
|
const accept = (req, server, gateway, context) => {
|
|
560
|
-
const
|
|
668
|
+
const fallback = unclaimed.get(gateway);
|
|
669
|
+
const data = {
|
|
670
|
+
path: gateway.path,
|
|
671
|
+
context,
|
|
672
|
+
id: crypto.randomUUID(),
|
|
673
|
+
[RUNTIME]: gateway,
|
|
674
|
+
...fallback === undefined ? {} : { [UNCLAIMED]: fallback }
|
|
675
|
+
};
|
|
561
676
|
return server.upgrade(req, { data }) ? undefined : new Response("Expected a WebSocket upgrade", { status: 426 });
|
|
562
677
|
};
|
|
563
678
|
const upgradeHandler = (gateway) => (req, server) => {
|
|
@@ -573,6 +688,7 @@ var buildWebSocket = (discovered, options = {}) => {
|
|
|
573
688
|
websocket,
|
|
574
689
|
routes: new Map(gateways.map((gateway) => [gateway.path, upgradeHandler(gateway)])),
|
|
575
690
|
paths: [...byPath.keys()],
|
|
691
|
+
warnings: middleware.length > 0 && !reports ? [unreported(middleware)] : [],
|
|
576
692
|
gateways: gateways.map((gateway) => ({
|
|
577
693
|
name: gateway.name,
|
|
578
694
|
path: gateway.path,
|
|
@@ -581,6 +697,105 @@ var buildWebSocket = (discovered, options = {}) => {
|
|
|
581
697
|
};
|
|
582
698
|
};
|
|
583
699
|
|
|
700
|
+
// src/ws/logging.ts
|
|
701
|
+
import { Logger, LogLevel, RequestContext } from "@dunx/core";
|
|
702
|
+
var LIFECYCLE_LABEL = {
|
|
703
|
+
[HandlerKind.OPEN]: "connect",
|
|
704
|
+
[HandlerKind.CLOSE]: "disconnect"
|
|
705
|
+
};
|
|
706
|
+
var elapsedMs = (started) => Math.round((Bun.nanoseconds() - started) / 1e6);
|
|
707
|
+
|
|
708
|
+
class SocketLoggingMiddleware {
|
|
709
|
+
logger;
|
|
710
|
+
context;
|
|
711
|
+
reportsErrors = true;
|
|
712
|
+
#level;
|
|
713
|
+
#errorLevel;
|
|
714
|
+
#events;
|
|
715
|
+
#lifecycle;
|
|
716
|
+
#payload;
|
|
717
|
+
#limit;
|
|
718
|
+
#correlate;
|
|
719
|
+
constructor(logger, context, options = {}) {
|
|
720
|
+
this.logger = logger;
|
|
721
|
+
this.context = context;
|
|
722
|
+
this.#level = options.level ?? LogLevel.DEBUG;
|
|
723
|
+
this.#errorLevel = options.errorLevel ?? LogLevel.ERROR;
|
|
724
|
+
this.#events = options.events ?? {};
|
|
725
|
+
this.#lifecycle = options.lifecycle ?? this.#level;
|
|
726
|
+
this.#payload = options.payload ?? false;
|
|
727
|
+
this.#limit = options.maxPayloadLength ?? 512;
|
|
728
|
+
this.#correlate = options.correlate ?? true;
|
|
729
|
+
}
|
|
730
|
+
#levelFor(ctx) {
|
|
731
|
+
if (ctx.kind !== HandlerKind.MESSAGE)
|
|
732
|
+
return this.#lifecycle;
|
|
733
|
+
if (ctx.event === undefined)
|
|
734
|
+
return this.#level;
|
|
735
|
+
return this.#events[ctx.event] ?? this.#level;
|
|
736
|
+
}
|
|
737
|
+
handle(frame, ctx, next) {
|
|
738
|
+
const level = this.#levelFor(ctx);
|
|
739
|
+
if (level === false)
|
|
740
|
+
return next();
|
|
741
|
+
const label = ctx.event ?? LIFECYCLE_LABEL[ctx.kind] ?? ctx.kind;
|
|
742
|
+
const connectionId = frame.socket.data.id;
|
|
743
|
+
const started = Bun.nanoseconds();
|
|
744
|
+
const write2 = (error, value) => {
|
|
745
|
+
const entry = {
|
|
746
|
+
gateway: ctx.gateway,
|
|
747
|
+
path: ctx.path,
|
|
748
|
+
event: label,
|
|
749
|
+
connectionId,
|
|
750
|
+
elapsedMs: elapsedMs(started),
|
|
751
|
+
...this.#payload ? { payload: this.#brief(frame.data) } : {},
|
|
752
|
+
...error === undefined ? { replied: value !== undefined } : { err: error }
|
|
753
|
+
};
|
|
754
|
+
const line = `${ctx.path} ${label}`;
|
|
755
|
+
this.#emit(error === undefined ? level : this.#errorLevel, line, entry);
|
|
756
|
+
};
|
|
757
|
+
if (!this.#correlate)
|
|
758
|
+
return observe(next, write2);
|
|
759
|
+
return this.context.runWithContext({ connectionId, event: label, flow: "ws", context: ctx.gateway }, () => observe(next, write2));
|
|
760
|
+
}
|
|
761
|
+
#emit(level, line, entry) {
|
|
762
|
+
switch (level) {
|
|
763
|
+
case LogLevel.VERBOSE:
|
|
764
|
+
this.logger.verbose(line, entry);
|
|
765
|
+
return;
|
|
766
|
+
case LogLevel.DEBUG:
|
|
767
|
+
this.logger.debug(line, entry);
|
|
768
|
+
return;
|
|
769
|
+
case LogLevel.INFO:
|
|
770
|
+
this.logger.info(line, entry);
|
|
771
|
+
return;
|
|
772
|
+
case LogLevel.WARN:
|
|
773
|
+
this.logger.warn(line, entry);
|
|
774
|
+
return;
|
|
775
|
+
case LogLevel.ERROR:
|
|
776
|
+
this.logger.error(line, entry);
|
|
777
|
+
return;
|
|
778
|
+
default:
|
|
779
|
+
this.logger.fatal(line, entry);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
#brief(data) {
|
|
783
|
+
if (data === undefined || this.#limit === 0)
|
|
784
|
+
return;
|
|
785
|
+
if (typeof data === "string") {
|
|
786
|
+
return data.length > this.#limit ? `[${data.length} chars]` : data;
|
|
787
|
+
}
|
|
788
|
+
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
|
|
789
|
+
return `[${data.byteLength} bytes]`;
|
|
790
|
+
}
|
|
791
|
+
const text = JSON.stringify(data) ?? "";
|
|
792
|
+
return text.length > this.#limit ? `[${text.length} chars]` : data;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
Object.defineProperty(SocketLoggingMiddleware, Symbol.for("dunx.deps"), {
|
|
796
|
+
value: () => [Logger, RequestContext, { unresolved: "options: SocketLoggingOptions = {}" }]
|
|
797
|
+
});
|
|
798
|
+
|
|
584
799
|
// src/ws/pubsub.ts
|
|
585
800
|
import { AppError as AppError5 } from "@dunx/core";
|
|
586
801
|
|
|
@@ -742,19 +957,40 @@ class PubSub {
|
|
|
742
957
|
// src/server/application.ts
|
|
743
958
|
import {
|
|
744
959
|
AppError as AppError7,
|
|
745
|
-
Logger as
|
|
960
|
+
Logger as Logger3,
|
|
746
961
|
runtimeInfo,
|
|
747
|
-
ShutdownHooks
|
|
962
|
+
ShutdownHooks,
|
|
963
|
+
teardownError,
|
|
964
|
+
teardownFailures as toFailures
|
|
748
965
|
} from "@dunx/core";
|
|
749
966
|
|
|
750
967
|
// src/server/request-logging.ts
|
|
751
968
|
import {
|
|
752
|
-
Logger,
|
|
753
|
-
RequestContext
|
|
969
|
+
Logger as Logger2,
|
|
970
|
+
RequestContext as RequestContext2
|
|
754
971
|
} from "@dunx/core";
|
|
972
|
+
|
|
973
|
+
// src/server/request-id.ts
|
|
755
974
|
var REQUEST_ID_HEADER = "x-request-id";
|
|
756
975
|
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
757
976
|
var traceId = (inbound) => inbound !== null && inbound.length === 36 && UUID.test(inbound) ? inbound : crypto.randomUUID();
|
|
977
|
+
var ID = Symbol.for("dunx.http.requestId");
|
|
978
|
+
|
|
979
|
+
class RequestIds {
|
|
980
|
+
static assign(req) {
|
|
981
|
+
const id = traceId(req.headers.get(REQUEST_ID_HEADER));
|
|
982
|
+
req[ID] = id;
|
|
983
|
+
return id;
|
|
984
|
+
}
|
|
985
|
+
static stamp(response, req) {
|
|
986
|
+
const id = req[ID];
|
|
987
|
+
if (id !== undefined)
|
|
988
|
+
response.headers.set(REQUEST_ID_HEADER, id);
|
|
989
|
+
return response;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// src/server/request-logging.ts
|
|
758
994
|
var parse = (text, limit) => {
|
|
759
995
|
if (limit === 0)
|
|
760
996
|
return;
|
|
@@ -768,7 +1004,7 @@ var parse = (text, limit) => {
|
|
|
768
1004
|
return text;
|
|
769
1005
|
}
|
|
770
1006
|
};
|
|
771
|
-
var
|
|
1007
|
+
var elapsedMs2 = (started) => Math.round((Bun.nanoseconds() - started) / 1e6);
|
|
772
1008
|
|
|
773
1009
|
class RequestLoggingMiddleware {
|
|
774
1010
|
logger;
|
|
@@ -807,7 +1043,7 @@ class RequestLoggingMiddleware {
|
|
|
807
1043
|
return this.#correlateIgnored ? this.#correlated(req, ctx, path, next) : next();
|
|
808
1044
|
}
|
|
809
1045
|
const started = Bun.nanoseconds();
|
|
810
|
-
const requestId =
|
|
1046
|
+
const requestId = RequestIds.assign(req);
|
|
811
1047
|
const scope = {
|
|
812
1048
|
requestId,
|
|
813
1049
|
method: ctx.method,
|
|
@@ -835,7 +1071,7 @@ class RequestLoggingMiddleware {
|
|
|
835
1071
|
});
|
|
836
1072
|
}
|
|
837
1073
|
#correlated(req, ctx, path, next) {
|
|
838
|
-
const requestId =
|
|
1074
|
+
const requestId = RequestIds.assign(req);
|
|
839
1075
|
const stamp = (response) => {
|
|
840
1076
|
response.headers.set(REQUEST_ID_HEADER, requestId);
|
|
841
1077
|
return response;
|
|
@@ -870,7 +1106,7 @@ class RequestLoggingMiddleware {
|
|
|
870
1106
|
request,
|
|
871
1107
|
err: error,
|
|
872
1108
|
statusCode: status,
|
|
873
|
-
elapsedMs:
|
|
1109
|
+
elapsedMs: elapsedMs2(started)
|
|
874
1110
|
};
|
|
875
1111
|
const line = `${req.method} ${path} ${status}`;
|
|
876
1112
|
if (status < HttpStatusCode.INTERNAL_SERVER_ERROR) {
|
|
@@ -886,7 +1122,7 @@ class RequestLoggingMiddleware {
|
|
|
886
1122
|
...scope,
|
|
887
1123
|
request,
|
|
888
1124
|
statusCode: response.status,
|
|
889
|
-
elapsedMs:
|
|
1125
|
+
elapsedMs: elapsedMs2(started)
|
|
890
1126
|
});
|
|
891
1127
|
response.headers.set(REQUEST_ID_HEADER, requestId);
|
|
892
1128
|
return response;
|
|
@@ -897,7 +1133,7 @@ class RequestLoggingMiddleware {
|
|
|
897
1133
|
request,
|
|
898
1134
|
statusCode: response.status,
|
|
899
1135
|
...value === undefined ? {} : { responseBody: value },
|
|
900
|
-
elapsedMs:
|
|
1136
|
+
elapsedMs: elapsedMs2(started)
|
|
901
1137
|
});
|
|
902
1138
|
response.headers.set(REQUEST_ID_HEADER, requestId);
|
|
903
1139
|
return response;
|
|
@@ -923,7 +1159,7 @@ class RequestLoggingMiddleware {
|
|
|
923
1159
|
}
|
|
924
1160
|
}
|
|
925
1161
|
Object.defineProperty(RequestLoggingMiddleware, Symbol.for("dunx.deps"), {
|
|
926
|
-
value: () => [
|
|
1162
|
+
value: () => [Logger2, RequestContext2, { unresolved: "options: RequestLoggingOptions = {}" }]
|
|
927
1163
|
});
|
|
928
1164
|
|
|
929
1165
|
// src/server/routes.ts
|
|
@@ -1089,7 +1325,7 @@ var buildFallback = (middleware = [], onError = defaultErrorMapper, cors, notFou
|
|
|
1089
1325
|
try {
|
|
1090
1326
|
return await compose(middleware, unmatchedContext(req, notFound === "public"), miss)(req);
|
|
1091
1327
|
} catch (error) {
|
|
1092
|
-
return onError(error, req);
|
|
1328
|
+
return RequestIds.stamp(onError(error, req), req);
|
|
1093
1329
|
}
|
|
1094
1330
|
};
|
|
1095
1331
|
return cors ? withCors(cors, run) : run;
|
|
@@ -1146,7 +1382,7 @@ var buildRoutes = (discovered, middleware = [], onError = defaultErrorMapper, co
|
|
|
1146
1382
|
try {
|
|
1147
1383
|
return await chained(req);
|
|
1148
1384
|
} catch (error) {
|
|
1149
|
-
return onError(error, req);
|
|
1385
|
+
return RequestIds.stamp(onError(error, req), req);
|
|
1150
1386
|
}
|
|
1151
1387
|
};
|
|
1152
1388
|
const byMethod = routes[route.path] ??= {};
|
|
@@ -1197,7 +1433,7 @@ class HttpApplication {
|
|
|
1197
1433
|
...options.requestLogging === false ? [] : [RequestLoggingMiddleware],
|
|
1198
1434
|
...options.middleware ?? []
|
|
1199
1435
|
];
|
|
1200
|
-
this.#onError = options.onError === undefined ? errorMapper(app.get(
|
|
1436
|
+
this.#onError = options.onError === undefined ? errorMapper(app.get(Logger3)) : toErrorMapper(options.onError, (token) => app.get(token, root));
|
|
1201
1437
|
this.#port = options.port ?? 3000;
|
|
1202
1438
|
this.#websocket = websocket;
|
|
1203
1439
|
this.#relay = options.relay;
|
|
@@ -1263,7 +1499,7 @@ class HttpApplication {
|
|
|
1263
1499
|
const pubsub = this.#app.get(PubSub);
|
|
1264
1500
|
pubsub.attach(this.#server);
|
|
1265
1501
|
if (this.#relay) {
|
|
1266
|
-
const logger = this.#app.get(
|
|
1502
|
+
const logger = this.#app.get(Logger3);
|
|
1267
1503
|
await pubsub.relayThrough(this.#relay, {
|
|
1268
1504
|
...this.#relayChannel !== undefined && {
|
|
1269
1505
|
channel: this.#relayChannel
|
|
@@ -1287,7 +1523,7 @@ class HttpApplication {
|
|
|
1287
1523
|
`${routes.length} route(s)`,
|
|
1288
1524
|
...gateways.length === 0 ? [] : [`${gateways.length} gateway(s)`]
|
|
1289
1525
|
].join(" and ");
|
|
1290
|
-
this.#app.get(
|
|
1526
|
+
this.#app.get(Logger3).info(`Serving ${subject}`, {
|
|
1291
1527
|
...runtimeInfo(),
|
|
1292
1528
|
routes: routes.map((route) => `${route.method} ${route.path}`),
|
|
1293
1529
|
...gateways.length === 0 ? {} : {
|
|
@@ -1304,12 +1540,25 @@ class HttpApplication {
|
|
|
1304
1540
|
}
|
|
1305
1541
|
async shutdown() {
|
|
1306
1542
|
this.#shuttingDown ??= (async () => {
|
|
1307
|
-
|
|
1308
|
-
|
|
1543
|
+
const failures = [];
|
|
1544
|
+
const step = async (run) => {
|
|
1545
|
+
try {
|
|
1546
|
+
await run();
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
failures.push(...toFailures(error));
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
await step(() => this.#app.drain());
|
|
1552
|
+
await step(async () => this.#server?.stop(this.#websocket !== undefined));
|
|
1309
1553
|
this.#server = undefined;
|
|
1310
|
-
await this.#app.get(PubSub).close();
|
|
1311
|
-
|
|
1312
|
-
|
|
1554
|
+
await step(() => this.#app.get(PubSub).close());
|
|
1555
|
+
try {
|
|
1556
|
+
await step(() => this.#app.shutdown());
|
|
1557
|
+
} finally {
|
|
1558
|
+
this.#resolveClosed?.();
|
|
1559
|
+
}
|
|
1560
|
+
if (failures.length > 0)
|
|
1561
|
+
throw teardownError(failures);
|
|
1313
1562
|
})();
|
|
1314
1563
|
return this.#shuttingDown;
|
|
1315
1564
|
}
|
|
@@ -1343,10 +1592,18 @@ class HttpFactory {
|
|
|
1343
1592
|
static async create(root, options = {}) {
|
|
1344
1593
|
const logging = provide(RequestLoggingMiddleware, {
|
|
1345
1594
|
useFactory: (logger, context) => new RequestLoggingMiddleware(logger, context, typeof options.requestLogging === "object" ? options.requestLogging : {}),
|
|
1346
|
-
inject: [
|
|
1595
|
+
inject: [Logger4, RequestContext3]
|
|
1596
|
+
});
|
|
1597
|
+
const socketLogging = provide(SocketLoggingMiddleware, {
|
|
1598
|
+
useFactory: (logger, context) => new SocketLoggingMiddleware(logger, context, typeof options.socketLogging === "object" ? options.socketLogging : {}),
|
|
1599
|
+
inject: [Logger4, RequestContext3]
|
|
1347
1600
|
});
|
|
1348
1601
|
const services = [PubSub, ClientAddress];
|
|
1349
|
-
const providers =
|
|
1602
|
+
const providers = [
|
|
1603
|
+
...services,
|
|
1604
|
+
...options.requestLogging === false ? [] : [logging],
|
|
1605
|
+
...options.socketLogging === false ? [] : [socketLogging]
|
|
1606
|
+
];
|
|
1350
1607
|
const scope = {
|
|
1351
1608
|
module: HttpModule,
|
|
1352
1609
|
global: true,
|
|
@@ -1375,9 +1632,17 @@ class HttpFactory {
|
|
|
1375
1632
|
}
|
|
1376
1633
|
assertNoCollisions(discovered);
|
|
1377
1634
|
const gateways = discoverGateways(modules, (token) => app.get(token));
|
|
1378
|
-
const websocket = gateways.length > 0 ? buildWebSocket(gateways, options.websocket) : undefined;
|
|
1635
|
+
const websocket = gateways.length > 0 ? buildWebSocket(gateways, options.websocket, HttpFactory.#socketMiddleware(app, root, options)) : undefined;
|
|
1636
|
+
for (const warning of websocket?.warnings ?? []) {
|
|
1637
|
+
app.get(Logger4).warn(warning);
|
|
1638
|
+
}
|
|
1379
1639
|
return new HttpApplication(app, discovered, options, root, websocket);
|
|
1380
1640
|
}
|
|
1641
|
+
static #socketMiddleware(app, root, options) {
|
|
1642
|
+
const declared = options.socketMiddleware ?? [];
|
|
1643
|
+
const entries = options.socketLogging === false ? declared : [SocketLoggingMiddleware, ...declared];
|
|
1644
|
+
return entries.map((entry) => app.get(entry, root));
|
|
1645
|
+
}
|
|
1381
1646
|
}
|
|
1382
1647
|
// src/static/files.ts
|
|
1383
1648
|
import { join, normalize, resolve } from "path";
|
|
@@ -1502,6 +1767,242 @@ StaticModule = __decorateElement(_init, 0, "StaticModule", _dec, StaticModule);
|
|
|
1502
1767
|
__runInitializers(_init, 1, StaticModule);
|
|
1503
1768
|
__decoratorMetadata(_init, StaticModule);
|
|
1504
1769
|
let _StaticModule = StaticModule;
|
|
1770
|
+
// src/throttle/decorators.ts
|
|
1771
|
+
var THROTTLE = metaKey("throttle");
|
|
1772
|
+
var SKIP_THROTTLE = metaKey("skip-throttle");
|
|
1773
|
+
var Throttle = (limit) => meta(THROTTLE, limit);
|
|
1774
|
+
var SkipThrottle = () => meta(SKIP_THROTTLE, true);
|
|
1775
|
+
// src/throttle/guard.ts
|
|
1776
|
+
import { Logger as Logger5 } from "@dunx/core";
|
|
1777
|
+
|
|
1778
|
+
// src/throttle/options.ts
|
|
1779
|
+
import { AppError as AppError9 } from "@dunx/core";
|
|
1780
|
+
|
|
1781
|
+
class ThrottleOptions {
|
|
1782
|
+
limit;
|
|
1783
|
+
windowSeconds;
|
|
1784
|
+
prefix;
|
|
1785
|
+
headers;
|
|
1786
|
+
subject;
|
|
1787
|
+
store;
|
|
1788
|
+
constructor(init) {
|
|
1789
|
+
if (init.prefix.trim() === "") {
|
|
1790
|
+
throw new AppError9("ThrottleModule needs a prefix naming this application, and it has no " + "default: two apps sharing one Redis with one throttle namespace each " + "spend the other's budget. Pass something like { prefix: 'orders-api' }.");
|
|
1791
|
+
}
|
|
1792
|
+
if (!Number.isInteger(init.limit) || init.limit < 1) {
|
|
1793
|
+
throw new AppError9(`ThrottleModule needs a limit of at least 1; got ${init.limit}.`);
|
|
1794
|
+
}
|
|
1795
|
+
if (!Number.isInteger(init.windowSeconds) || init.windowSeconds < 1) {
|
|
1796
|
+
throw new AppError9("ThrottleModule needs a windowSeconds of at least 1; got " + `${init.windowSeconds}.`);
|
|
1797
|
+
}
|
|
1798
|
+
this.limit = init.limit;
|
|
1799
|
+
this.windowSeconds = init.windowSeconds;
|
|
1800
|
+
this.prefix = init.prefix;
|
|
1801
|
+
this.headers = init.headers ?? true;
|
|
1802
|
+
this.subject = init.subject;
|
|
1803
|
+
this.store = init.store;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
Object.defineProperty(ThrottleOptions, Symbol.for("dunx.deps"), {
|
|
1807
|
+
value: () => [{ unresolved: "init: ThrottleOptionsInit" }]
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
// src/throttle/store.ts
|
|
1811
|
+
import { AppError as AppError10 } from "@dunx/core";
|
|
1812
|
+
|
|
1813
|
+
class ThrottleStore {
|
|
1814
|
+
constructor() {
|
|
1815
|
+
if (new.target === ThrottleStore) {
|
|
1816
|
+
throw new AppError10("ThrottleStore is a contract, not an implementation. Bind one with " + "ThrottleModule.forRoot({ store: new RedisThrottleStore(redis) }), or " + "leave it out for the in-process MemoryThrottleStore.");
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
class RedisThrottleStore extends ThrottleStore {
|
|
1822
|
+
redis;
|
|
1823
|
+
constructor(redis) {
|
|
1824
|
+
super();
|
|
1825
|
+
this.redis = redis;
|
|
1826
|
+
}
|
|
1827
|
+
async hit(key, windowSeconds) {
|
|
1828
|
+
const used = await this.redis.incr(key);
|
|
1829
|
+
if (used === 1)
|
|
1830
|
+
await this.redis.expire(key, windowSeconds);
|
|
1831
|
+
return used;
|
|
1832
|
+
}
|
|
1833
|
+
async ttl(key) {
|
|
1834
|
+
const left = await this.redis.ttl(key);
|
|
1835
|
+
return left > 0 ? left : undefined;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
Object.defineProperty(RedisThrottleStore, Symbol.for("dunx.deps"), {
|
|
1839
|
+
value: () => [{ unresolved: "private readonly redis: ThrottleRedis" }]
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
class MemoryThrottleStore extends ThrottleStore {
|
|
1843
|
+
#windows = new Map;
|
|
1844
|
+
#maxKeys;
|
|
1845
|
+
constructor(maxKeys = 1e4) {
|
|
1846
|
+
super();
|
|
1847
|
+
this.#maxKeys = maxKeys;
|
|
1848
|
+
}
|
|
1849
|
+
hit(key, windowSeconds) {
|
|
1850
|
+
const now = Date.now();
|
|
1851
|
+
const existing = this.#windows.get(key);
|
|
1852
|
+
if (existing !== undefined && existing.expiresAt > now) {
|
|
1853
|
+
existing.count += 1;
|
|
1854
|
+
return Promise.resolve(existing.count);
|
|
1855
|
+
}
|
|
1856
|
+
if (this.#windows.size >= this.#maxKeys)
|
|
1857
|
+
this.#sweep(now);
|
|
1858
|
+
this.#windows.set(key, { count: 1, expiresAt: now + windowSeconds * 1000 });
|
|
1859
|
+
return Promise.resolve(1);
|
|
1860
|
+
}
|
|
1861
|
+
ttl(key) {
|
|
1862
|
+
const window = this.#windows.get(key);
|
|
1863
|
+
if (window === undefined)
|
|
1864
|
+
return Promise.resolve(undefined);
|
|
1865
|
+
const left = Math.ceil((window.expiresAt - Date.now()) / 1000);
|
|
1866
|
+
return Promise.resolve(left > 0 ? left : undefined);
|
|
1867
|
+
}
|
|
1868
|
+
#sweep(now) {
|
|
1869
|
+
for (const [key, window] of this.#windows) {
|
|
1870
|
+
if (window.expiresAt <= now)
|
|
1871
|
+
this.#windows.delete(key);
|
|
1872
|
+
}
|
|
1873
|
+
if (this.#windows.size >= this.#maxKeys)
|
|
1874
|
+
this.#windows.clear();
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
Object.defineProperty(MemoryThrottleStore, Symbol.for("dunx.deps"), {
|
|
1878
|
+
value: () => [{ unresolved: "maxKeys = 10_000" }]
|
|
1879
|
+
});
|
|
1880
|
+
|
|
1881
|
+
// src/throttle/guard.ts
|
|
1882
|
+
class ThrottleGuard {
|
|
1883
|
+
options;
|
|
1884
|
+
store;
|
|
1885
|
+
address;
|
|
1886
|
+
logger;
|
|
1887
|
+
#warned = false;
|
|
1888
|
+
constructor(options, store, address, logger) {
|
|
1889
|
+
this.options = options;
|
|
1890
|
+
this.store = store;
|
|
1891
|
+
this.address = address;
|
|
1892
|
+
this.logger = logger;
|
|
1893
|
+
}
|
|
1894
|
+
async handle(req, ctx, next) {
|
|
1895
|
+
if (ctx.get(UNMATCHED) === true)
|
|
1896
|
+
return next();
|
|
1897
|
+
if (ctx.get(SKIP_THROTTLE) === true)
|
|
1898
|
+
return next();
|
|
1899
|
+
const limit = ctx.get(THROTTLE) ?? this.options;
|
|
1900
|
+
const key = this.#key(req, ctx);
|
|
1901
|
+
const used = await this.#hit(key, limit.windowSeconds);
|
|
1902
|
+
if (used === undefined)
|
|
1903
|
+
return next();
|
|
1904
|
+
if (used > limit.limit) {
|
|
1905
|
+
const after = await this.#ttl(key) ?? limit.windowSeconds;
|
|
1906
|
+
throw new HttpError(HttpStatusCode.TOO_MANY_REQUESTS, `Rate limit exceeded: ${limit.limit} requests per ` + `${limit.windowSeconds}s`, this.options.headers ? {
|
|
1907
|
+
headers: {
|
|
1908
|
+
"retry-after": String(after),
|
|
1909
|
+
"ratelimit-limit": String(limit.limit),
|
|
1910
|
+
"ratelimit-remaining": "0",
|
|
1911
|
+
"ratelimit-reset": String(after)
|
|
1912
|
+
}
|
|
1913
|
+
} : undefined);
|
|
1914
|
+
}
|
|
1915
|
+
const response = await next();
|
|
1916
|
+
if (this.options.headers) {
|
|
1917
|
+
response.headers.set("ratelimit-limit", String(limit.limit));
|
|
1918
|
+
response.headers.set("ratelimit-remaining", String(Math.max(0, limit.limit - used)));
|
|
1919
|
+
}
|
|
1920
|
+
return response;
|
|
1921
|
+
}
|
|
1922
|
+
#key(req, ctx) {
|
|
1923
|
+
const subject = (this.options.subject ?? ((request) => this.address.of(request)))(req, ctx) ?? "anonymous";
|
|
1924
|
+
return `${this.options.prefix}:throttle:${ctx.controller}:${ctx.handler}:${subject}`;
|
|
1925
|
+
}
|
|
1926
|
+
async#hit(key, windowSeconds) {
|
|
1927
|
+
try {
|
|
1928
|
+
return await this.store.hit(key, windowSeconds);
|
|
1929
|
+
} catch (error) {
|
|
1930
|
+
this.#degraded(error);
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
async#ttl(key) {
|
|
1935
|
+
try {
|
|
1936
|
+
return await this.store.ttl(key);
|
|
1937
|
+
} catch (error) {
|
|
1938
|
+
this.#degraded(error);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
#degraded(error) {
|
|
1943
|
+
if (this.#warned)
|
|
1944
|
+
return;
|
|
1945
|
+
this.#warned = true;
|
|
1946
|
+
this.logger.warn("The rate limiter is unreachable, so requests are not being counted.", { reason: error.message });
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), {
|
|
1950
|
+
value: () => [ThrottleOptions, ThrottleStore, ClientAddress, Logger5]
|
|
1951
|
+
});
|
|
1952
|
+
// src/throttle/module.ts
|
|
1953
|
+
import {
|
|
1954
|
+
Logger as Logger6,
|
|
1955
|
+
Module as Module2,
|
|
1956
|
+
provide as provide3
|
|
1957
|
+
} from "@dunx/core";
|
|
1958
|
+
var EXPORTS = [ThrottleOptions, ThrottleStore, ThrottleGuard];
|
|
1959
|
+
var guard = () => provide3(ThrottleGuard, {
|
|
1960
|
+
useFactory: (options, store, address, logger) => new ThrottleGuard(options, store, address, logger),
|
|
1961
|
+
inject: [ThrottleOptions, ThrottleStore, ClientAddress, Logger6]
|
|
1962
|
+
});
|
|
1963
|
+
var store = () => provide3(ThrottleStore, {
|
|
1964
|
+
useFactory: (options) => options.store ?? new MemoryThrottleStore,
|
|
1965
|
+
inject: [ThrottleOptions]
|
|
1966
|
+
});
|
|
1967
|
+
var _dec = [
|
|
1968
|
+
Module2({})
|
|
1969
|
+
];
|
|
1970
|
+
var _init = __decoratorStart(undefined);
|
|
1971
|
+
|
|
1972
|
+
class ThrottleModule {
|
|
1973
|
+
static forRoot(init) {
|
|
1974
|
+
return {
|
|
1975
|
+
module: ThrottleModule,
|
|
1976
|
+
global: true,
|
|
1977
|
+
exports: EXPORTS,
|
|
1978
|
+
providers: [
|
|
1979
|
+
provide3(ThrottleOptions, { useValue: new ThrottleOptions(init) }),
|
|
1980
|
+
store(),
|
|
1981
|
+
guard()
|
|
1982
|
+
]
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
static forRootAsync(config) {
|
|
1986
|
+
return {
|
|
1987
|
+
module: ThrottleModule,
|
|
1988
|
+
global: true,
|
|
1989
|
+
...config.imports && { imports: config.imports },
|
|
1990
|
+
exports: EXPORTS,
|
|
1991
|
+
providers: [
|
|
1992
|
+
provide3(ThrottleOptions, {
|
|
1993
|
+
useFactory: async (...deps) => new ThrottleOptions(await config.useFactory(...deps)),
|
|
1994
|
+
inject: config.inject ?? []
|
|
1995
|
+
}),
|
|
1996
|
+
store(),
|
|
1997
|
+
guard()
|
|
1998
|
+
]
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
ThrottleModule = __decorateElement(_init, 0, "ThrottleModule", _dec, ThrottleModule);
|
|
2003
|
+
__runInitializers(_init, 1, ThrottleModule);
|
|
2004
|
+
__decoratorMetadata(_init, ThrottleModule);
|
|
2005
|
+
let _ThrottleModule = ThrottleModule;
|
|
1505
2006
|
// src/ws/decorators.ts
|
|
1506
2007
|
var Gateway = (path = "/") => (target) => {
|
|
1507
2008
|
markGateway(target, path);
|
|
@@ -1522,7 +2023,7 @@ var OnMessage = (event) => (value) => {
|
|
|
1522
2023
|
return value;
|
|
1523
2024
|
};
|
|
1524
2025
|
// src/ws/redis-relay.ts
|
|
1525
|
-
import { AppError as
|
|
2026
|
+
import { AppError as AppError11 } from "@dunx/core";
|
|
1526
2027
|
var PROTOCOLS = [
|
|
1527
2028
|
"redis:",
|
|
1528
2029
|
"rediss:",
|
|
@@ -1538,10 +2039,10 @@ var assertUrl = (url) => {
|
|
|
1538
2039
|
try {
|
|
1539
2040
|
parsed = new URL(url);
|
|
1540
2041
|
} catch {
|
|
1541
|
-
throw new
|
|
2042
|
+
throw new AppError11(`${JSON.stringify(url)} is not a valid URL for the websocket relay. ` + "Expected something like redis://localhost:6379.");
|
|
1542
2043
|
}
|
|
1543
2044
|
if (!PROTOCOLS.includes(parsed.protocol)) {
|
|
1544
|
-
throw new
|
|
2045
|
+
throw new AppError11(`Unsupported protocol ${JSON.stringify(parsed.protocol)} in ` + `${JSON.stringify(url)}. Expected one of ${PROTOCOLS.join(", ")}.`);
|
|
1545
2046
|
}
|
|
1546
2047
|
return url;
|
|
1547
2048
|
};
|
|
@@ -1677,7 +2178,7 @@ Object.defineProperty(HealthOptions, Symbol.for("dunx.deps"), {
|
|
|
1677
2178
|
class HealthRegistry {
|
|
1678
2179
|
options;
|
|
1679
2180
|
readiness_;
|
|
1680
|
-
#startedAt =
|
|
2181
|
+
#startedAt = performance.now();
|
|
1681
2182
|
constructor(options, readiness_) {
|
|
1682
2183
|
this.options = options;
|
|
1683
2184
|
this.readiness_ = readiness_;
|
|
@@ -1697,7 +2198,7 @@ class HealthRegistry {
|
|
|
1697
2198
|
return {
|
|
1698
2199
|
status: worst(checks),
|
|
1699
2200
|
draining: this.readiness_.draining,
|
|
1700
|
-
uptimeMs:
|
|
2201
|
+
uptimeMs: Math.round(performance.now() - this.#startedAt),
|
|
1701
2202
|
checks
|
|
1702
2203
|
};
|
|
1703
2204
|
}
|
|
@@ -1867,8 +2368,8 @@ Object.defineProperty(DiskIndicator, Symbol.for("dunx.deps"), {
|
|
|
1867
2368
|
});
|
|
1868
2369
|
// src/health/module.ts
|
|
1869
2370
|
import {
|
|
1870
|
-
Module as
|
|
1871
|
-
provide as
|
|
2371
|
+
Module as Module3,
|
|
2372
|
+
provide as provide4
|
|
1872
2373
|
} from "@dunx/core";
|
|
1873
2374
|
|
|
1874
2375
|
// src/health/readiness.ts
|
|
@@ -1915,22 +2416,22 @@ Object.defineProperty(Readiness, Symbol.for("dunx.deps"), {
|
|
|
1915
2416
|
// src/health/module.ts
|
|
1916
2417
|
var wiring = (options) => [
|
|
1917
2418
|
...options,
|
|
1918
|
-
|
|
2419
|
+
provide4(ReadinessOptions, {
|
|
1919
2420
|
useFactory: (opts) => new ReadinessOptions({ drainDelayMs: opts.drainDelayMs }),
|
|
1920
2421
|
inject: [HealthOptions]
|
|
1921
2422
|
}),
|
|
1922
|
-
|
|
2423
|
+
provide4(Readiness, {
|
|
1923
2424
|
useFactory: (opts) => new Readiness(opts),
|
|
1924
2425
|
inject: [ReadinessOptions]
|
|
1925
2426
|
}),
|
|
1926
|
-
|
|
2427
|
+
provide4(HealthRegistry, {
|
|
1927
2428
|
useFactory: (opts, readiness) => new HealthRegistry(opts, readiness),
|
|
1928
2429
|
inject: [HealthOptions, Readiness]
|
|
1929
2430
|
})
|
|
1930
2431
|
];
|
|
1931
2432
|
var surface = [HealthOptions, HealthRegistry, Readiness];
|
|
1932
2433
|
var _dec = [
|
|
1933
|
-
|
|
2434
|
+
Module3({})
|
|
1934
2435
|
];
|
|
1935
2436
|
var _init = __decoratorStart(undefined);
|
|
1936
2437
|
|
|
@@ -1941,7 +2442,7 @@ class HealthModule {
|
|
|
1941
2442
|
module: HealthModule,
|
|
1942
2443
|
...options.routes ? { controllers: [HealthController] } : {},
|
|
1943
2444
|
exports: surface,
|
|
1944
|
-
providers: wiring([
|
|
2445
|
+
providers: wiring([provide4(HealthOptions, { useValue: options })])
|
|
1945
2446
|
};
|
|
1946
2447
|
}
|
|
1947
2448
|
static forRootAsync(config) {
|
|
@@ -1951,7 +2452,7 @@ class HealthModule {
|
|
|
1951
2452
|
...config.routes ?? true ? { controllers: [HealthController] } : {},
|
|
1952
2453
|
exports: surface,
|
|
1953
2454
|
providers: wiring([
|
|
1954
|
-
|
|
2455
|
+
provide4(HealthOptions, {
|
|
1955
2456
|
useFactory: async (...deps) => new HealthOptions(await config.useFactory(...deps)),
|
|
1956
2457
|
inject: config.inject ?? []
|
|
1957
2458
|
})
|
|
@@ -1969,6 +2470,7 @@ export {
|
|
|
1969
2470
|
toErrorMapper,
|
|
1970
2471
|
routesOf,
|
|
1971
2472
|
preflight,
|
|
2473
|
+
observe,
|
|
1972
2474
|
normalizePrefix,
|
|
1973
2475
|
normalizePath,
|
|
1974
2476
|
metaOf,
|
|
@@ -1990,6 +2492,7 @@ export {
|
|
|
1990
2492
|
defaultErrorMapper,
|
|
1991
2493
|
decodeRelay,
|
|
1992
2494
|
decode,
|
|
2495
|
+
composeSocket,
|
|
1993
2496
|
compose,
|
|
1994
2497
|
buildWebSocket,
|
|
1995
2498
|
buildRuntime,
|
|
@@ -2001,11 +2504,21 @@ export {
|
|
|
2001
2504
|
ValidationError,
|
|
2002
2505
|
UseGuards,
|
|
2003
2506
|
UNMATCHED,
|
|
2507
|
+
ThrottleStore,
|
|
2508
|
+
ThrottleOptions,
|
|
2509
|
+
ThrottleModule,
|
|
2510
|
+
ThrottleGuard,
|
|
2511
|
+
Throttle,
|
|
2512
|
+
THROTTLE,
|
|
2004
2513
|
StaticOptions,
|
|
2005
2514
|
StaticModule,
|
|
2006
2515
|
StaticFiles,
|
|
2516
|
+
SocketLoggingMiddleware,
|
|
2517
|
+
SkipThrottle,
|
|
2518
|
+
SKIP_THROTTLE,
|
|
2007
2519
|
Roles,
|
|
2008
2520
|
RequestLoggingMiddleware,
|
|
2521
|
+
RedisThrottleStore,
|
|
2009
2522
|
RedisRelay,
|
|
2010
2523
|
RedisIndicator,
|
|
2011
2524
|
ReadinessOptions,
|
|
@@ -2027,6 +2540,7 @@ export {
|
|
|
2027
2540
|
OnMessage,
|
|
2028
2541
|
OnDrain,
|
|
2029
2542
|
OnClose,
|
|
2543
|
+
MemoryThrottleStore,
|
|
2030
2544
|
MemoryOptions,
|
|
2031
2545
|
MemoryIndicator,
|
|
2032
2546
|
HttpStatusCode,
|
|
@@ -2052,5 +2566,5 @@ export {
|
|
|
2052
2566
|
ApiHidden
|
|
2053
2567
|
};
|
|
2054
2568
|
|
|
2055
|
-
//# debugId=
|
|
2569
|
+
//# debugId=C7EBB73765DCC48764756E2164756E21
|
|
2056
2570
|
//# sourceMappingURL=index.js.map
|