@dunx/http 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-sz4pvqxy.js → chunk-jh7jk0bn.js} +56 -3
- package/dist/client/options.d.ts +12 -0
- package/dist/client.js +14 -5
- package/dist/compression/compression.d.ts +30 -0
- package/dist/compression/module.d.ts +20 -0
- package/dist/compression/negotiate.d.ts +12 -0
- package/dist/compression/options.d.ts +73 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +309 -20
- package/dist/route/decorators.d.ts +6 -6
- package/dist/route/marker.d.ts +13 -0
- package/dist/route/schema.d.ts +64 -6
- package/dist/server/request-logging.d.ts +14 -0
- package/dist/server/routes.d.ts +1 -1
- package/dist/server/trace-context.d.ts +51 -0
- package/package.json +3 -3
- package/dist/chunk-sz4pvqxy.js.map +0 -10
- package/dist/client.js.map +0 -15
- package/dist/index.js.map +0 -53
package/dist/index.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
HttpStatusCode,
|
|
4
|
+
TRACEPARENT_HEADER,
|
|
5
|
+
TRACESTATE_HEADER,
|
|
6
|
+
TraceContext,
|
|
4
7
|
__decorateElement,
|
|
5
8
|
__decoratorMetadata,
|
|
6
9
|
__decoratorStart,
|
|
7
10
|
__privateAdd,
|
|
8
11
|
__privateGet,
|
|
9
12
|
__runInitializers
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-jh7jk0bn.js";
|
|
11
14
|
|
|
12
15
|
// src/route/marker.ts
|
|
13
16
|
var ROUTE = Symbol.for("dunx.route");
|
|
14
17
|
var CONTROLLER = Symbol.for("dunx.controller");
|
|
18
|
+
var defaultStatusFor = (method) => method === "POST" ? HttpStatusCode.CREATED : HttpStatusCode.OK;
|
|
15
19
|
var resolvePath = (path) => typeof path === "function" ? path() : path;
|
|
16
20
|
var markRoute = (target, meta) => {
|
|
17
21
|
Object.defineProperty(target, ROUTE, { value: meta, configurable: true });
|
|
@@ -1036,6 +1040,7 @@ class RequestLoggingMiddleware {
|
|
|
1036
1040
|
#ignorePrefix;
|
|
1037
1041
|
#correlateIgnored;
|
|
1038
1042
|
#correlate;
|
|
1043
|
+
#trace;
|
|
1039
1044
|
constructor(logger, context, options = {}) {
|
|
1040
1045
|
this.logger = logger;
|
|
1041
1046
|
this.context = context;
|
|
@@ -1046,6 +1051,7 @@ class RequestLoggingMiddleware {
|
|
|
1046
1051
|
this.#ignorePrefix = options.ignorePrefix ?? [];
|
|
1047
1052
|
this.#correlateIgnored = options.correlateIgnored ?? false;
|
|
1048
1053
|
this.#correlate = options.correlate ?? true;
|
|
1054
|
+
this.#trace = options.trace ?? false;
|
|
1049
1055
|
}
|
|
1050
1056
|
#ignored(path) {
|
|
1051
1057
|
if (this.#ignore.size > 0 && this.#ignore.has(path))
|
|
@@ -1071,6 +1077,14 @@ class RequestLoggingMiddleware {
|
|
|
1071
1077
|
flow: "http",
|
|
1072
1078
|
context: `${ctx.controller}.${ctx.handler}`
|
|
1073
1079
|
};
|
|
1080
|
+
if (this.#trace) {
|
|
1081
|
+
const trace = TraceContext.adopt(req, requestId);
|
|
1082
|
+
scope.traceId = trace.traceId;
|
|
1083
|
+
scope.spanId = trace.spanId;
|
|
1084
|
+
if (trace.parentSpanId !== undefined) {
|
|
1085
|
+
scope.parentSpanId = trace.parentSpanId;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1074
1088
|
return this.#correlate ? this.context.runWithContext(scope, () => this.#begin(req, ctx, url, mark, path, requestId, started, next, undefined)) : this.#begin(req, ctx, url, mark, path, requestId, started, next, scope);
|
|
1075
1089
|
}
|
|
1076
1090
|
#begin(req, ctx, url, mark, path, requestId, started, next, scope) {
|
|
@@ -1319,7 +1333,7 @@ var toResponse = (value, status) => {
|
|
|
1319
1333
|
}
|
|
1320
1334
|
return Response.json(value, { status });
|
|
1321
1335
|
};
|
|
1322
|
-
var statusFor = (route) => route.options?.status ?? (route.method
|
|
1336
|
+
var statusFor = (route) => route.options?.status ?? defaultStatusFor(route.method);
|
|
1323
1337
|
var assertNoCollisions = (discovered) => {
|
|
1324
1338
|
const owners = new Map;
|
|
1325
1339
|
for (const route of discovered) {
|
|
@@ -1810,6 +1824,274 @@ StaticModule = __decorateElement(_init, 0, "StaticModule", _dec, StaticModule);
|
|
|
1810
1824
|
__runInitializers(_init, 1, StaticModule);
|
|
1811
1825
|
__decoratorMetadata(_init, StaticModule);
|
|
1812
1826
|
let _StaticModule = StaticModule;
|
|
1827
|
+
// src/compression/negotiate.ts
|
|
1828
|
+
var quality = (params) => {
|
|
1829
|
+
for (const param of params) {
|
|
1830
|
+
const [key, value] = param.split("=");
|
|
1831
|
+
if (key?.trim().toLowerCase() !== "q")
|
|
1832
|
+
continue;
|
|
1833
|
+
const q = Number.parseFloat(value ?? "");
|
|
1834
|
+
return Number.isFinite(q) && q >= 0 && q <= 1 ? q : 1;
|
|
1835
|
+
}
|
|
1836
|
+
return 1;
|
|
1837
|
+
};
|
|
1838
|
+
var negotiate = (header, offered) => {
|
|
1839
|
+
if (header === null)
|
|
1840
|
+
return;
|
|
1841
|
+
const accepted = new Map;
|
|
1842
|
+
for (const element of header.split(",")) {
|
|
1843
|
+
const [name, ...params] = element.split(";");
|
|
1844
|
+
const token = name?.trim().toLowerCase();
|
|
1845
|
+
if (token === undefined || token === "")
|
|
1846
|
+
continue;
|
|
1847
|
+
accepted.set(token, quality(params));
|
|
1848
|
+
}
|
|
1849
|
+
const wildcard = accepted.get("*");
|
|
1850
|
+
let best;
|
|
1851
|
+
let bestQuality = 0;
|
|
1852
|
+
for (const encoding of offered) {
|
|
1853
|
+
const q = accepted.get(encoding) ?? wildcard ?? 0;
|
|
1854
|
+
if (q > bestQuality) {
|
|
1855
|
+
best = encoding;
|
|
1856
|
+
bestQuality = q;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
return best;
|
|
1860
|
+
};
|
|
1861
|
+
|
|
1862
|
+
// src/compression/options.ts
|
|
1863
|
+
var CompressionEncoding = Object.freeze({
|
|
1864
|
+
ZSTD: "zstd",
|
|
1865
|
+
GZIP: "gzip"
|
|
1866
|
+
});
|
|
1867
|
+
var COMPRESSIBLE = new Set([
|
|
1868
|
+
"application/graphql",
|
|
1869
|
+
"application/graphql-response+json",
|
|
1870
|
+
"application/javascript",
|
|
1871
|
+
"application/json",
|
|
1872
|
+
"application/manifest+json",
|
|
1873
|
+
"application/wasm",
|
|
1874
|
+
"application/x-javascript",
|
|
1875
|
+
"application/x-ndjson",
|
|
1876
|
+
"application/xml",
|
|
1877
|
+
"image/svg+xml"
|
|
1878
|
+
]);
|
|
1879
|
+
var isCompressibleType = (contentType) => {
|
|
1880
|
+
if (contentType === null)
|
|
1881
|
+
return false;
|
|
1882
|
+
const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
1883
|
+
if (type.startsWith("text/"))
|
|
1884
|
+
return true;
|
|
1885
|
+
if (type.endsWith("+json") || type.endsWith("+xml"))
|
|
1886
|
+
return true;
|
|
1887
|
+
return COMPRESSIBLE.has(type);
|
|
1888
|
+
};
|
|
1889
|
+
var encodable = (encoding) => {
|
|
1890
|
+
const sync = encoding === CompressionEncoding.ZSTD ? Bun.zstdCompressSync : Bun.gzipSync;
|
|
1891
|
+
if (typeof sync !== "function")
|
|
1892
|
+
return false;
|
|
1893
|
+
try {
|
|
1894
|
+
new CompressionStream(encoding);
|
|
1895
|
+
return true;
|
|
1896
|
+
} catch {
|
|
1897
|
+
return false;
|
|
1898
|
+
}
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
class CompressionOptions {
|
|
1902
|
+
encodings;
|
|
1903
|
+
threshold;
|
|
1904
|
+
filter;
|
|
1905
|
+
constructor(init = {}) {
|
|
1906
|
+
this.encodings = init.encodings ?? [
|
|
1907
|
+
CompressionEncoding.ZSTD,
|
|
1908
|
+
CompressionEncoding.GZIP
|
|
1909
|
+
];
|
|
1910
|
+
const missing = this.encodings.filter((encoding) => !encodable(encoding));
|
|
1911
|
+
if (missing.length > 0) {
|
|
1912
|
+
throw new Error(`Bun ${Bun.version} cannot encode ${missing.join(", ")}. ` + "Pass `encodings` without it, or upgrade Bun.");
|
|
1913
|
+
}
|
|
1914
|
+
this.threshold = init.threshold ?? 1024;
|
|
1915
|
+
this.filter = init.filter ?? isCompressibleType;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
Object.defineProperty(CompressionOptions, Symbol.for("dunx.deps"), {
|
|
1919
|
+
value: () => [{ unresolved: "init: CompressionOptionsInit = {}" }]
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
// src/compression/compression.ts
|
|
1923
|
+
var BODYLESS = new Set([204, 205, 304]);
|
|
1924
|
+
var BUFFER_LIMIT = 1024 * 1024;
|
|
1925
|
+
var buffer = async (body, limit) => {
|
|
1926
|
+
const reader = body.getReader();
|
|
1927
|
+
const chunks = [];
|
|
1928
|
+
let size = 0;
|
|
1929
|
+
for (;; ) {
|
|
1930
|
+
const { done, value } = await reader.read();
|
|
1931
|
+
if (done)
|
|
1932
|
+
break;
|
|
1933
|
+
chunks.push(value);
|
|
1934
|
+
size += value.byteLength;
|
|
1935
|
+
if (size > limit) {
|
|
1936
|
+
return {
|
|
1937
|
+
rest: new ReadableStream({
|
|
1938
|
+
start: (controller) => {
|
|
1939
|
+
for (const chunk of chunks)
|
|
1940
|
+
controller.enqueue(chunk);
|
|
1941
|
+
},
|
|
1942
|
+
pull: async (controller) => {
|
|
1943
|
+
const next = await reader.read();
|
|
1944
|
+
if (next.done)
|
|
1945
|
+
controller.close();
|
|
1946
|
+
else
|
|
1947
|
+
controller.enqueue(next.value);
|
|
1948
|
+
},
|
|
1949
|
+
cancel: (reason) => reader.cancel(reason)
|
|
1950
|
+
})
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
const bytes = new Uint8Array(size);
|
|
1955
|
+
let offset = 0;
|
|
1956
|
+
for (const chunk of chunks) {
|
|
1957
|
+
bytes.set(chunk, offset);
|
|
1958
|
+
offset += chunk.byteLength;
|
|
1959
|
+
}
|
|
1960
|
+
return { bytes };
|
|
1961
|
+
};
|
|
1962
|
+
var declaredLength = (headers) => {
|
|
1963
|
+
const raw = headers.get("content-length");
|
|
1964
|
+
if (raw === null)
|
|
1965
|
+
return;
|
|
1966
|
+
const length = Number.parseInt(raw, 10);
|
|
1967
|
+
return Number.isFinite(length) ? length : undefined;
|
|
1968
|
+
};
|
|
1969
|
+
var weakenETag = (headers) => {
|
|
1970
|
+
const etag = headers.get("etag");
|
|
1971
|
+
if (etag !== null && !etag.startsWith("W/"))
|
|
1972
|
+
headers.set("etag", `W/${etag}`);
|
|
1973
|
+
};
|
|
1974
|
+
var varyOnEncoding = (headers) => {
|
|
1975
|
+
const existing = headers.get("vary");
|
|
1976
|
+
if (existing === null) {
|
|
1977
|
+
headers.set("vary", "accept-encoding");
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
if (existing.trim() === "*")
|
|
1981
|
+
return;
|
|
1982
|
+
const listed = existing.split(",").some((field) => field.trim().toLowerCase() === "accept-encoding");
|
|
1983
|
+
if (!listed)
|
|
1984
|
+
headers.set("vary", `${existing}, accept-encoding`);
|
|
1985
|
+
};
|
|
1986
|
+
var encodeSync = (encoding, data) => {
|
|
1987
|
+
switch (encoding) {
|
|
1988
|
+
case CompressionEncoding.ZSTD:
|
|
1989
|
+
return Bun.zstdCompressSync(data);
|
|
1990
|
+
case CompressionEncoding.GZIP:
|
|
1991
|
+
return Bun.gzipSync(data);
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
class Compression {
|
|
1996
|
+
#options;
|
|
1997
|
+
constructor(options) {
|
|
1998
|
+
this.#options = options;
|
|
1999
|
+
}
|
|
2000
|
+
#considers(res) {
|
|
2001
|
+
if (BODYLESS.has(res.status) || res.status === 206)
|
|
2002
|
+
return false;
|
|
2003
|
+
if (res.headers.has("content-encoding"))
|
|
2004
|
+
return false;
|
|
2005
|
+
if (res.headers.get("cache-control")?.includes("no-transform") === true) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
return this.#options.filter(res.headers.get("content-type"));
|
|
2009
|
+
}
|
|
2010
|
+
async handle(req, _ctx, next) {
|
|
2011
|
+
const res = await next();
|
|
2012
|
+
const body = res.body;
|
|
2013
|
+
if (body === null || !this.#considers(res))
|
|
2014
|
+
return res;
|
|
2015
|
+
varyOnEncoding(res.headers);
|
|
2016
|
+
const encoding = negotiate(req.headers.get("accept-encoding"), this.#options.encodings);
|
|
2017
|
+
if (encoding === undefined)
|
|
2018
|
+
return res;
|
|
2019
|
+
const declared = declaredLength(res.headers);
|
|
2020
|
+
if (declared !== undefined && declared < this.#options.threshold)
|
|
2021
|
+
return res;
|
|
2022
|
+
const headers = new Headers(res.headers);
|
|
2023
|
+
headers.set("content-encoding", encoding);
|
|
2024
|
+
weakenETag(headers);
|
|
2025
|
+
const source = declared !== undefined && declared > BUFFER_LIMIT ? { rest: body } : await buffer(body, BUFFER_LIMIT);
|
|
2026
|
+
if ("rest" in source) {
|
|
2027
|
+
headers.delete("content-length");
|
|
2028
|
+
return new Response(source.rest.pipeThrough(new CompressionStream(encoding)), { status: res.status, statusText: res.statusText, headers });
|
|
2029
|
+
}
|
|
2030
|
+
if (source.bytes.byteLength < this.#options.threshold) {
|
|
2031
|
+
const passthrough = new Headers(res.headers);
|
|
2032
|
+
passthrough.set("content-length", String(source.bytes.byteLength));
|
|
2033
|
+
return new Response(source.bytes, {
|
|
2034
|
+
status: res.status,
|
|
2035
|
+
statusText: res.statusText,
|
|
2036
|
+
headers: passthrough
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
const encoded = encodeSync(encoding, source.bytes);
|
|
2040
|
+
headers.set("content-length", String(encoded.byteLength));
|
|
2041
|
+
return new Response(encoded, {
|
|
2042
|
+
status: res.status,
|
|
2043
|
+
statusText: res.statusText,
|
|
2044
|
+
headers
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
Object.defineProperty(Compression, Symbol.for("dunx.deps"), {
|
|
2049
|
+
value: () => [CompressionOptions]
|
|
2050
|
+
});
|
|
2051
|
+
// src/compression/module.ts
|
|
2052
|
+
import {
|
|
2053
|
+
Module as Module2,
|
|
2054
|
+
provide as provide3
|
|
2055
|
+
} from "@dunx/core";
|
|
2056
|
+
var middleware = () => provide3(Compression, {
|
|
2057
|
+
useFactory: (options) => new Compression(options),
|
|
2058
|
+
inject: [CompressionOptions]
|
|
2059
|
+
});
|
|
2060
|
+
var _dec = [
|
|
2061
|
+
Module2({})
|
|
2062
|
+
];
|
|
2063
|
+
var _init = __decoratorStart(undefined);
|
|
2064
|
+
|
|
2065
|
+
class CompressionModule {
|
|
2066
|
+
static forRoot(init = {}) {
|
|
2067
|
+
return {
|
|
2068
|
+
module: CompressionModule,
|
|
2069
|
+
exports: [CompressionOptions, Compression],
|
|
2070
|
+
providers: [
|
|
2071
|
+
provide3(CompressionOptions, { useValue: new CompressionOptions(init) }),
|
|
2072
|
+
middleware()
|
|
2073
|
+
]
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
static forRootAsync(config) {
|
|
2077
|
+
return {
|
|
2078
|
+
module: CompressionModule,
|
|
2079
|
+
...config.imports && { imports: config.imports },
|
|
2080
|
+
exports: [CompressionOptions, Compression],
|
|
2081
|
+
providers: [
|
|
2082
|
+
provide3(CompressionOptions, {
|
|
2083
|
+
useFactory: async (...deps) => new CompressionOptions(await config.useFactory(...deps)),
|
|
2084
|
+
inject: config.inject ?? []
|
|
2085
|
+
}),
|
|
2086
|
+
middleware()
|
|
2087
|
+
]
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
CompressionModule = __decorateElement(_init, 0, "CompressionModule", _dec, CompressionModule);
|
|
2092
|
+
__runInitializers(_init, 1, CompressionModule);
|
|
2093
|
+
__decoratorMetadata(_init, CompressionModule);
|
|
2094
|
+
let _CompressionModule = CompressionModule;
|
|
1813
2095
|
// src/throttle/decorators.ts
|
|
1814
2096
|
var THROTTLE = metaKey("throttle");
|
|
1815
2097
|
var SKIP_THROTTLE = metaKey("skip-throttle");
|
|
@@ -1995,20 +2277,20 @@ Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), {
|
|
|
1995
2277
|
// src/throttle/module.ts
|
|
1996
2278
|
import {
|
|
1997
2279
|
Logger as Logger6,
|
|
1998
|
-
Module as
|
|
1999
|
-
provide as
|
|
2280
|
+
Module as Module3,
|
|
2281
|
+
provide as provide4
|
|
2000
2282
|
} from "@dunx/core";
|
|
2001
2283
|
var EXPORTS = [ThrottleOptions, ThrottleStore, ThrottleGuard];
|
|
2002
|
-
var guard = () =>
|
|
2284
|
+
var guard = () => provide4(ThrottleGuard, {
|
|
2003
2285
|
useFactory: (options, store, address, logger) => new ThrottleGuard(options, store, address, logger),
|
|
2004
2286
|
inject: [ThrottleOptions, ThrottleStore, ClientAddress, Logger6]
|
|
2005
2287
|
});
|
|
2006
|
-
var store = () =>
|
|
2288
|
+
var store = () => provide4(ThrottleStore, {
|
|
2007
2289
|
useFactory: (options) => options.store ?? new MemoryThrottleStore,
|
|
2008
2290
|
inject: [ThrottleOptions]
|
|
2009
2291
|
});
|
|
2010
2292
|
var _dec = [
|
|
2011
|
-
|
|
2293
|
+
Module3({})
|
|
2012
2294
|
];
|
|
2013
2295
|
var _init = __decoratorStart(undefined);
|
|
2014
2296
|
|
|
@@ -2019,7 +2301,7 @@ class ThrottleModule {
|
|
|
2019
2301
|
global: true,
|
|
2020
2302
|
exports: EXPORTS,
|
|
2021
2303
|
providers: [
|
|
2022
|
-
|
|
2304
|
+
provide4(ThrottleOptions, { useValue: new ThrottleOptions(init) }),
|
|
2023
2305
|
store(),
|
|
2024
2306
|
guard()
|
|
2025
2307
|
]
|
|
@@ -2032,7 +2314,7 @@ class ThrottleModule {
|
|
|
2032
2314
|
...config.imports && { imports: config.imports },
|
|
2033
2315
|
exports: EXPORTS,
|
|
2034
2316
|
providers: [
|
|
2035
|
-
|
|
2317
|
+
provide4(ThrottleOptions, {
|
|
2036
2318
|
useFactory: async (...deps) => new ThrottleOptions(await config.useFactory(...deps)),
|
|
2037
2319
|
inject: config.inject ?? []
|
|
2038
2320
|
}),
|
|
@@ -2471,8 +2753,8 @@ Object.defineProperty(DiskIndicator, Symbol.for("dunx.deps"), {
|
|
|
2471
2753
|
});
|
|
2472
2754
|
// src/health/module.ts
|
|
2473
2755
|
import {
|
|
2474
|
-
Module as
|
|
2475
|
-
provide as
|
|
2756
|
+
Module as Module4,
|
|
2757
|
+
provide as provide5
|
|
2476
2758
|
} from "@dunx/core";
|
|
2477
2759
|
|
|
2478
2760
|
// src/health/readiness.ts
|
|
@@ -2519,15 +2801,15 @@ Object.defineProperty(Readiness, Symbol.for("dunx.deps"), {
|
|
|
2519
2801
|
// src/health/module.ts
|
|
2520
2802
|
var wiring = (options) => [
|
|
2521
2803
|
...options,
|
|
2522
|
-
|
|
2804
|
+
provide5(ReadinessOptions, {
|
|
2523
2805
|
useFactory: (opts) => new ReadinessOptions({ drainDelayMs: opts.drainDelayMs }),
|
|
2524
2806
|
inject: [HealthOptions]
|
|
2525
2807
|
}),
|
|
2526
|
-
|
|
2808
|
+
provide5(Readiness, {
|
|
2527
2809
|
useFactory: (opts) => new Readiness(opts),
|
|
2528
2810
|
inject: [ReadinessOptions]
|
|
2529
2811
|
}),
|
|
2530
|
-
|
|
2812
|
+
provide5(HealthRegistry, {
|
|
2531
2813
|
useFactory: (opts, readiness) => new HealthRegistry(opts, readiness),
|
|
2532
2814
|
inject: [HealthOptions, Readiness]
|
|
2533
2815
|
})
|
|
@@ -2535,7 +2817,7 @@ var wiring = (options) => [
|
|
|
2535
2817
|
var surface = [HealthOptions, HealthRegistry, Readiness];
|
|
2536
2818
|
var controllerFor = (documented) => documented ? HealthController : HiddenHealthController;
|
|
2537
2819
|
var _dec = [
|
|
2538
|
-
|
|
2820
|
+
Module4({})
|
|
2539
2821
|
];
|
|
2540
2822
|
var _init = __decoratorStart(undefined);
|
|
2541
2823
|
|
|
@@ -2546,7 +2828,7 @@ class HealthModule {
|
|
|
2546
2828
|
module: HealthModule,
|
|
2547
2829
|
...options.routes ? { controllers: [controllerFor(options.documented)] } : {},
|
|
2548
2830
|
exports: surface,
|
|
2549
|
-
providers: wiring([
|
|
2831
|
+
providers: wiring([provide5(HealthOptions, { useValue: options })])
|
|
2550
2832
|
};
|
|
2551
2833
|
}
|
|
2552
2834
|
static forRootAsync(config) {
|
|
@@ -2556,7 +2838,7 @@ class HealthModule {
|
|
|
2556
2838
|
...config.routes ?? true ? { controllers: [controllerFor(config.documented ?? true)] } : {},
|
|
2557
2839
|
exports: surface,
|
|
2558
2840
|
providers: wiring([
|
|
2559
|
-
|
|
2841
|
+
provide5(HealthOptions, {
|
|
2560
2842
|
useFactory: async (...deps) => new HealthOptions(await config.useFactory(...deps)),
|
|
2561
2843
|
inject: config.inject ?? []
|
|
2562
2844
|
})
|
|
@@ -2571,6 +2853,10 @@ let _HealthModule = HealthModule;
|
|
|
2571
2853
|
export {
|
|
2572
2854
|
ApiHidden,
|
|
2573
2855
|
ClientAddress,
|
|
2856
|
+
Compression,
|
|
2857
|
+
CompressionEncoding,
|
|
2858
|
+
CompressionModule,
|
|
2859
|
+
CompressionOptions,
|
|
2574
2860
|
Controller,
|
|
2575
2861
|
DEFAULT_RELAY_CHANNEL,
|
|
2576
2862
|
DatabaseIndicator,
|
|
@@ -2626,11 +2912,14 @@ export {
|
|
|
2626
2912
|
StaticModule,
|
|
2627
2913
|
StaticOptions,
|
|
2628
2914
|
THROTTLE,
|
|
2915
|
+
TRACEPARENT_HEADER,
|
|
2916
|
+
TRACESTATE_HEADER,
|
|
2629
2917
|
Throttle,
|
|
2630
2918
|
ThrottleGuard,
|
|
2631
2919
|
ThrottleModule,
|
|
2632
2920
|
ThrottleOptions,
|
|
2633
2921
|
ThrottleStore,
|
|
2922
|
+
TraceContext,
|
|
2634
2923
|
UNMATCHED,
|
|
2635
2924
|
UseGuards,
|
|
2636
2925
|
ValidationError,
|
|
@@ -2647,6 +2936,7 @@ export {
|
|
|
2647
2936
|
decodeRelay,
|
|
2648
2937
|
defaultErrorMapper,
|
|
2649
2938
|
defaultRelayUrl,
|
|
2939
|
+
defaultStatusFor,
|
|
2650
2940
|
discoverGateway,
|
|
2651
2941
|
discoverGateways,
|
|
2652
2942
|
discoverRoutes,
|
|
@@ -2655,6 +2945,7 @@ export {
|
|
|
2655
2945
|
errorMapper,
|
|
2656
2946
|
gatewaysOf,
|
|
2657
2947
|
guardsOf,
|
|
2948
|
+
isCompressibleType,
|
|
2658
2949
|
isErrorFilter,
|
|
2659
2950
|
isGateway,
|
|
2660
2951
|
joinPath,
|
|
@@ -2662,6 +2953,7 @@ export {
|
|
|
2662
2953
|
meta,
|
|
2663
2954
|
metaKey,
|
|
2664
2955
|
metaOf,
|
|
2956
|
+
negotiate,
|
|
2665
2957
|
normalizePath,
|
|
2666
2958
|
normalizePrefix,
|
|
2667
2959
|
observe,
|
|
@@ -2671,6 +2963,3 @@ export {
|
|
|
2671
2963
|
withCors,
|
|
2672
2964
|
withUpgradeRoutes
|
|
2673
2965
|
};
|
|
2674
|
-
|
|
2675
|
-
//# debugId=1A8278039B82BC0664756E2164756E21
|
|
2676
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type RoutePath } from './marker.js';
|
|
2
|
-
import type { Input, RouteSchemas } from './schema.js';
|
|
2
|
+
import type { Input, Returns, RouteSchemas } from './schema.js';
|
|
3
3
|
type ControllerTarget = abstract new (...args: never[]) => object;
|
|
4
4
|
export declare const Controller: (prefix?: string) => <T extends ControllerTarget>(target: T) => T;
|
|
5
|
-
export declare const Get: <const O extends RouteSchemas>(path?: RoutePath, options?: O) => <
|
|
6
|
-
export declare const Post: <const O extends RouteSchemas>(path?: RoutePath, options?: O) => <
|
|
7
|
-
export declare const Put: <const O extends RouteSchemas>(path?: RoutePath, options?: O) => <
|
|
8
|
-
export declare const Patch: <const O extends RouteSchemas>(path?: RoutePath, options?: O) => <
|
|
9
|
-
export declare const Delete: <const O extends RouteSchemas>(path?: RoutePath, options?: O) => <
|
|
5
|
+
export declare const Get: <const O extends RouteSchemas>(path?: RoutePath, options?: O | undefined) => <H extends (input: Input<O>) => Returns<O, "GET">>(value: H, _context: ClassMethodDecoratorContext) => H;
|
|
6
|
+
export declare const Post: <const O extends RouteSchemas>(path?: RoutePath, options?: O | undefined) => <H extends (input: Input<O>) => Returns<O, "POST">>(value: H, _context: ClassMethodDecoratorContext) => H;
|
|
7
|
+
export declare const Put: <const O extends RouteSchemas>(path?: RoutePath, options?: O | undefined) => <H extends (input: Input<O>) => Returns<O, "PUT">>(value: H, _context: ClassMethodDecoratorContext) => H;
|
|
8
|
+
export declare const Patch: <const O extends RouteSchemas>(path?: RoutePath, options?: O | undefined) => <H extends (input: Input<O>) => Returns<O, "PATCH">>(value: H, _context: ClassMethodDecoratorContext) => H;
|
|
9
|
+
export declare const Delete: <const O extends RouteSchemas>(path?: RoutePath, options?: O | undefined) => <H extends (input: Input<O>) => Returns<O, "DELETE">>(value: H, _context: ClassMethodDecoratorContext) => H;
|
|
10
10
|
export {};
|
package/dist/route/marker.d.ts
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import { HttpStatusCode } from '../server/status.js';
|
|
1
2
|
import type { RouteSchemas } from './schema.js';
|
|
2
3
|
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
|
+
/**
|
|
5
|
+
* The success status a route answers with when `options.status` is absent.
|
|
6
|
+
* `buildRoutes` and `@dunx/openapi`'s `statusOf` both read it, so the rule is
|
|
7
|
+
* stated once rather than in each of them.
|
|
8
|
+
*/
|
|
9
|
+
export declare const defaultStatusFor: (method: HttpMethod) => number;
|
|
10
|
+
/**
|
|
11
|
+
* The type-level twin of {@link defaultStatusFor}, derived from the same constants
|
|
12
|
+
* so the two cannot drift. `Returns` needs it to know which `response` entry a
|
|
13
|
+
* handler is being held to.
|
|
14
|
+
*/
|
|
15
|
+
export type DefaultStatus<M extends HttpMethod> = M extends 'POST' ? typeof HttpStatusCode.CREATED : typeof HttpStatusCode.OK;
|
|
3
16
|
/**
|
|
4
17
|
* A literal path, or a thunk read at **discovery** rather than at decoration.
|
|
5
18
|
*
|
package/dist/route/schema.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BunRequest } from 'bun';
|
|
2
|
+
import type { DefaultStatus, HttpMethod } from './marker.js';
|
|
2
3
|
/**
|
|
3
4
|
* Standard Schema v1, restated rather than depended on. The spec is an
|
|
4
5
|
* *interface*, not a runtime: `@standard-schema/spec` ships nothing but these
|
|
@@ -65,11 +66,13 @@ export interface RouteSchemas {
|
|
|
65
66
|
* } as const satisfies RouteSchemas;
|
|
66
67
|
* ```
|
|
67
68
|
*
|
|
68
|
-
* **Never validated
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
69
|
+
* **Never validated at runtime, checked at compile time.** Running a validation
|
|
70
|
+
* pass over every response body would be a per-request cost paid for a
|
|
71
|
+
* documentation feature. The handler's own return type carries the check
|
|
72
|
+
* instead: the verb decorators constrain it against the entry for the success
|
|
73
|
+
* status, so a handler answering with a different shape is a `TS1241` naming
|
|
74
|
+
* the mismatched property. See {@link Returns}. Nothing in the request path
|
|
75
|
+
* reads this key.
|
|
73
76
|
*
|
|
74
77
|
* A plain {@link JsonSchema} is accepted here too, and only here: a JSON Schema
|
|
75
78
|
* needs no conversion, so documenting a response costs no validator. `$id` names
|
|
@@ -83,8 +86,10 @@ export interface RouteSchemas {
|
|
|
83
86
|
* }
|
|
84
87
|
* ```
|
|
85
88
|
*/
|
|
86
|
-
readonly response?:
|
|
89
|
+
readonly response?: ResponseMap;
|
|
87
90
|
}
|
|
91
|
+
/** `response` keyed by status code. Named so {@link Returns} can constrain it. */
|
|
92
|
+
export type ResponseMap = Readonly<Record<number, StandardSchemaV1 | JsonSchema>>;
|
|
88
93
|
/**
|
|
89
94
|
* The handler's parameter type, derived from its own options object. It has to be
|
|
90
95
|
* written out - a standard method decorator can *check* a parameter's type but
|
|
@@ -118,6 +123,58 @@ export type Input<O extends RouteSchemas> = {
|
|
|
118
123
|
} ? {
|
|
119
124
|
readonly params: InferOutput<P>;
|
|
120
125
|
} : unknown);
|
|
126
|
+
/**
|
|
127
|
+
* The status a handler's return type is held to: an explicit `options.status`,
|
|
128
|
+
* else the verb's default. Widened to `number` without `as const`, which is what
|
|
129
|
+
* turns the check off rather than misapplying it.
|
|
130
|
+
*/
|
|
131
|
+
type SuccessStatus<O extends RouteSchemas, M extends HttpMethod> = O extends {
|
|
132
|
+
status: infer S extends number;
|
|
133
|
+
} ? S : DefaultStatus<M>;
|
|
134
|
+
/**
|
|
135
|
+
* A plain {@link JsonSchema} carries no type to infer, so it becomes `unknown` and
|
|
136
|
+
* absorbs whatever the handler returns. That is the escape hatch for a response
|
|
137
|
+
* whose shape no schema value describes.
|
|
138
|
+
*/
|
|
139
|
+
type Declared<S> = [InferOutput<S>] extends [never] ? unknown : Serialised<InferOutput<S>>;
|
|
140
|
+
/**
|
|
141
|
+
* The declared shape as JSON will present it, which is the same shape with every
|
|
142
|
+
* array made readonly.
|
|
143
|
+
*
|
|
144
|
+
* `z.array()` infers a mutable `T[]`, and `readonly T[]` is not assignable to it -
|
|
145
|
+
* so a repository method returning `readonly User[]`, the correct signature for
|
|
146
|
+
* something that must not be mutated, would fail against a document it satisfies.
|
|
147
|
+
* Mutability does not survive `Response.json`, so it is not part of the contract.
|
|
148
|
+
*
|
|
149
|
+
* Only arrays need the rewrite; TypeScript already ignores a property's `readonly`
|
|
150
|
+
* modifier when checking assignability. The object branch is how nested arrays are
|
|
151
|
+
* reached, and functions are returned untouched because mapping over one would
|
|
152
|
+
* discard its call signature.
|
|
153
|
+
*/
|
|
154
|
+
type Serialised<T> = T extends readonly (infer E)[] ? readonly Serialised<E>[] : T extends (...args: never[]) => unknown ? T : T extends object ? {
|
|
155
|
+
readonly [K in keyof T]: Serialised<T[K]>;
|
|
156
|
+
} : T;
|
|
157
|
+
/**
|
|
158
|
+
* What a handler may return, given its own options object and its verb.
|
|
159
|
+
*
|
|
160
|
+
* A route decorator can *check* a handler's type but cannot *infer* it
|
|
161
|
+
* (docs/architecture/constraints.md), and that cuts both ways: this is the return
|
|
162
|
+
* half of the same guarantee `Input<O>` gives the parameter. Declaring
|
|
163
|
+
* `response: { 200: User }` stops being documentation a handler can contradict.
|
|
164
|
+
*
|
|
165
|
+
* `Response` is always allowed - it is the escape hatch `buildRoutes` passes
|
|
166
|
+
* through untouched. So is a promise of either. Nothing is checked when the
|
|
167
|
+
* success status has no `response` entry.
|
|
168
|
+
*/
|
|
169
|
+
export type Returns<O extends RouteSchemas, M extends HttpMethod> = SuccessBody<O, M> | Response | Promise<SuccessBody<O, M> | Response>;
|
|
170
|
+
/**
|
|
171
|
+
* `infer R extends ResponseMap` is load bearing: without the constraint the
|
|
172
|
+
* narrowed `O` inside the branch is `{ response: R } & O`, whose `response` no
|
|
173
|
+
* longer satisfies `RouteSchemas`, and `SuccessStatus<O, M>` fails with `TS2344`.
|
|
174
|
+
*/
|
|
175
|
+
type SuccessBody<O extends RouteSchemas, M extends HttpMethod> = O extends {
|
|
176
|
+
response: infer R extends ResponseMap;
|
|
177
|
+
} ? SuccessStatus<O, M> extends keyof R ? Declared<R[SuccessStatus<O, M>]> : unknown : unknown;
|
|
121
178
|
/** What the framework actually hands a handler; `Input<O>` is its typed view. */
|
|
122
179
|
export interface RouteInput {
|
|
123
180
|
readonly req: BunRequest;
|
|
@@ -125,3 +182,4 @@ export interface RouteInput {
|
|
|
125
182
|
readonly query?: unknown;
|
|
126
183
|
readonly params?: unknown;
|
|
127
184
|
}
|
|
185
|
+
export {};
|
|
@@ -97,6 +97,20 @@ export interface RequestLoggingOptions {
|
|
|
97
97
|
* is for.
|
|
98
98
|
*/
|
|
99
99
|
readonly correlate?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Adopt W3C Trace Context, so `traceId`, `spanId` and `parentSpanId` join
|
|
102
|
+
* `requestId` on every line the request writes. Default **`false`**.
|
|
103
|
+
*
|
|
104
|
+
* On, an inbound `traceparent` is honoured and this service becomes a child
|
|
105
|
+
* span of the caller's; off, nothing reads the header and nothing is minted.
|
|
106
|
+
* It costs a header read and 8 random bytes per request, which is not worth
|
|
107
|
+
* paying in a service with nothing to correlate against - and `requestId`
|
|
108
|
+
* already spans two dunx services on its own.
|
|
109
|
+
*
|
|
110
|
+
* `@dunx/http/client` sends the adopted trace upstream, so turning this on at
|
|
111
|
+
* both ends is what makes one trace cover both.
|
|
112
|
+
*/
|
|
113
|
+
readonly trace?: boolean;
|
|
100
114
|
}
|
|
101
115
|
/**
|
|
102
116
|
* One structured entry per request, carrying the request and its response.
|
package/dist/server/routes.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Ctor, type ModuleRef } from '@dunx/core';
|
|
2
2
|
import type { DiscoveredRoute } from '../route/discover.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type HttpMethod } from '../route/marker.js';
|
|
4
4
|
import type { UpgradeHandler } from '../ws/adapter.js';
|
|
5
5
|
import { type CorsOptions } from './cors.js';
|
|
6
6
|
import { type ErrorMapper } from './errors.js';
|