@dunx/http 2.4.0 → 3.0.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/README.md +31 -909
- package/dist/chunk-25g22350.js +58 -0
- package/dist/chunk-ywdpxbkf.js +1573 -0
- package/dist/client/module.d.ts +2 -7
- package/dist/client/options.d.ts +12 -0
- package/dist/client/service.d.ts +11 -22
- package/dist/client.d.ts +8 -2
- package/dist/client.js +26 -137
- package/dist/compression/compression.d.ts +25 -0
- package/dist/compression/module.d.ts +20 -0
- package/dist/compression/negotiate.d.ts +12 -0
- package/dist/compression/options.d.ts +61 -0
- package/dist/health/indicators.d.ts +5 -10
- package/dist/index.d.ts +24 -20
- package/dist/index.js +325 -1366
- package/dist/internal.d.ts +36 -0
- package/dist/internal.js +89 -0
- package/dist/route/decorators.d.ts +6 -6
- package/dist/route/marker.d.ts +13 -0
- package/dist/route/metadata.d.ts +6 -9
- package/dist/route/schema.d.ts +59 -29
- package/dist/server/application.d.ts +33 -89
- package/dist/server/client-address.d.ts +6 -13
- package/dist/server/errors.d.ts +24 -55
- package/dist/server/request-id.d.ts +6 -11
- package/dist/server/request-logging.d.ts +33 -91
- package/dist/server/routes.d.ts +8 -20
- package/dist/server/trace-context.d.ts +47 -0
- package/dist/static/files.d.ts +5 -13
- package/dist/static/module.d.ts +10 -19
- package/dist/throttle/guard.d.ts +6 -9
- package/dist/throttle/module.d.ts +6 -8
- package/dist/throttle/store.d.ts +11 -20
- package/dist/ws/middleware.d.ts +12 -21
- package/dist/ws/redis-relay.d.ts +9 -19
- package/package.json +7 -3
- package/dist/chunk-sz4pvqxy.js +0 -111
- package/dist/chunk-sz4pvqxy.js.map +0 -10
- package/dist/client.js.map +0 -15
- package/dist/index.js.map +0 -53
package/dist/client/module.d.ts
CHANGED
|
@@ -41,13 +41,8 @@ export declare class HttpModule {
|
|
|
41
41
|
*/
|
|
42
42
|
static forRoot(init?: HttpClientOptionsInit): DynamicModule;
|
|
43
43
|
/**
|
|
44
|
-
* `forRoot` with the options behind a factory,
|
|
45
|
-
*
|
|
46
|
-
* `ConfigService`.
|
|
47
|
-
*
|
|
48
|
-
* There is no separate async machinery - the container resolves eagerly and
|
|
49
|
-
* awaits factories before any constructor runs, so awaited config is settled by
|
|
50
|
-
* the time anything is built.
|
|
44
|
+
* `forRoot` with the options behind a factory, so the base url or the timeout
|
|
45
|
+
* can come off `ConfigService`.
|
|
51
46
|
*
|
|
52
47
|
* ```ts
|
|
53
48
|
* HttpModule.forRootAsync({
|
package/dist/client/options.d.ts
CHANGED
|
@@ -24,6 +24,17 @@ export interface HttpClientOptionsInit {
|
|
|
24
24
|
* @default true
|
|
25
25
|
*/
|
|
26
26
|
readonly propagateRequestId?: boolean | string;
|
|
27
|
+
/**
|
|
28
|
+
* Forward W3C Trace Context upstream as `traceparent`, so the callee's spans
|
|
29
|
+
* join this request's trace.
|
|
30
|
+
*
|
|
31
|
+
* Read from `RequestContext`, so it only carries when a trace is in scope -
|
|
32
|
+
* which means `requestLogging: { trace: true }` on the inbound side. With that
|
|
33
|
+
* off there is nothing to send and this costs one property read.
|
|
34
|
+
*
|
|
35
|
+
* @default true
|
|
36
|
+
*/
|
|
37
|
+
readonly propagateTrace?: boolean;
|
|
27
38
|
/** Bound as its own token, so a second client can be injected by name. */
|
|
28
39
|
readonly name?: string;
|
|
29
40
|
/**
|
|
@@ -51,6 +62,7 @@ export declare class HttpClientOptions {
|
|
|
51
62
|
readonly headers: Readonly<Record<string, string>>;
|
|
52
63
|
readonly retry: RetryOptions<unknown>;
|
|
53
64
|
readonly requestIdHeader: string | undefined;
|
|
65
|
+
readonly propagateTrace: boolean;
|
|
54
66
|
readonly name: string | undefined;
|
|
55
67
|
readonly fetchOptions: Readonly<Record<string, unknown>>;
|
|
56
68
|
constructor(init?: HttpClientOptionsInit);
|
package/dist/client/service.d.ts
CHANGED
|
@@ -43,18 +43,12 @@ export interface RequestConfig<TRequest = unknown, TResponse = unknown> {
|
|
|
43
43
|
type BaseOptions<TRequest, TResponse> = Omit<RequestConfig<TRequest, TResponse>, 'method' | 'url' | 'payload'>;
|
|
44
44
|
/**
|
|
45
45
|
* A `fetch` client with a per-request timeout, retry with backoff, request-id
|
|
46
|
-
* propagation and one log line per call.
|
|
46
|
+
* propagation and one log line per call. `fetch` and nothing else, so there is no
|
|
47
|
+
* client dependency; what it adds is the parts every caller otherwise
|
|
48
|
+
* reimplements - the timeout, the retry policy, `Retry-After`, url building, and
|
|
49
|
+
* a failure that says which call failed.
|
|
47
50
|
*
|
|
48
|
-
* `
|
|
49
|
-
* is no client dependency to justify - which is also why `axios` and `node-fetch`
|
|
50
|
-
* are banned repo-wide. What this adds over calling `fetch` yourself is the parts
|
|
51
|
-
* every caller otherwise reimplements slightly differently: the timeout, the
|
|
52
|
-
* retry policy, `Retry-After`, url building, and a failure that says which call
|
|
53
|
-
* failed.
|
|
54
|
-
*
|
|
55
|
-
* Extends `UrlHelper` from `@arkv/shared`, so `buildUrl` and `interpolate` are
|
|
56
|
-
* available on the service, and there is one implementation of them across the
|
|
57
|
-
* owner's projects rather than a fork per repo.
|
|
51
|
+
* Extends `UrlHelper` from `@arkv/shared` for `buildUrl` and `interpolate`.
|
|
58
52
|
*/
|
|
59
53
|
export declare class HttpService extends UrlHelper {
|
|
60
54
|
private readonly options;
|
|
@@ -83,18 +77,13 @@ export declare class HttpService extends UrlHelper {
|
|
|
83
77
|
readonly method?: 'GET' | 'POST';
|
|
84
78
|
}): AsyncGenerator<string>;
|
|
85
79
|
/**
|
|
86
|
-
* Resolves the target
|
|
87
|
-
*
|
|
88
|
-
* `path`.
|
|
89
|
-
*
|
|
90
|
-
* `get('/users')` is the one worth calling out. A relative first argument is what
|
|
91
|
-
* every HTTP client takes once a base url exists, and passing it straight to
|
|
92
|
-
* `buildUrl` throws `ERR_INVALID_URL` from inside `new URL()` - a message naming
|
|
93
|
-
* neither the call nor the missing base. So a first argument that is not an
|
|
94
|
-
* absolute url is treated as the path, which is what it reads as.
|
|
80
|
+
* Resolves the target: an absolute url, a path relative to `baseUrl`, or
|
|
81
|
+
* `baseUrl` plus an explicit `path`.
|
|
95
82
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
83
|
+
* A relative first argument reaching `buildUrl` throws `ERR_INVALID_URL` from
|
|
84
|
+
* inside `new URL()`, naming neither the call nor the missing base, so one that
|
|
85
|
+
* is not absolute is treated as the path. `URL.canParse` decides rather than a
|
|
86
|
+
* regex, so it cannot disagree with `new URL`.
|
|
98
87
|
*/
|
|
99
88
|
private urlFor;
|
|
100
89
|
/** `serialised` is what a `headerFactory` signs, and is `''` for no body. */
|
package/dist/client.d.ts
CHANGED
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
* `@dunx/http` does not load any of this.
|
|
7
7
|
*/
|
|
8
8
|
export { FetchError, FetchTransportError } from './client/errors.js';
|
|
9
|
-
export { isJsonBody, isPlainObject, safeStringify } from './client/json.js';
|
|
10
9
|
export { DEFAULT_REQUEST_ID_HEADER, HttpClientOptions, type HttpClientOptionsInit, } from './client/options.js';
|
|
11
|
-
export {
|
|
10
|
+
export type { BackoffOptions, RetryOptions } from './client/retry.js';
|
|
12
11
|
export { httpClient, HttpModule } from './client/module.js';
|
|
13
12
|
export { HttpService, type HeaderFactory, type RequestConfig, type RequestMethod, } from './client/service.js';
|
|
13
|
+
/**
|
|
14
|
+
* The client's own plumbing, still reachable here and moving out in 4.0.
|
|
15
|
+
* Import it from `@dunx/http/internal`, which carries no stability promise.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated Import from `@dunx/http/internal`. Removed in 4.0.
|
|
18
|
+
*/
|
|
19
|
+
export { backoffDelay, executeWithRetry, isJsonBody, isPlainObject, isRetryableStatus, retryAfterMs, safeStringify, } from './internal.js';
|
package/dist/client.js
CHANGED
|
@@ -1,71 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.status = status;
|
|
18
|
-
this.statusText = statusText;
|
|
19
|
-
this.body = body;
|
|
20
|
-
this.response = response;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
Object.defineProperty(FetchError, Symbol.for("dunx.deps"), {
|
|
24
|
-
value: () => [{ unresolved: "readonly status: number" }, { unresolved: "readonly statusText: string" }, { unresolved: "readonly body: unknown" }, { unresolved: `readonly response: {
|
|
25
|
-
readonly method: string;
|
|
26
|
-
readonly url: string;
|
|
27
|
-
readonly headers: Headers;
|
|
28
|
-
}` }]
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
class FetchTransportError extends AppError {
|
|
32
|
-
response;
|
|
33
|
-
aborted;
|
|
34
|
-
name = "FetchTransportError";
|
|
35
|
-
constructor(response, aborted, options) {
|
|
36
|
-
super(`${response.method} ${response.url} failed: ${aborted ? "aborted" : "transport error"}`, options);
|
|
37
|
-
this.response = response;
|
|
38
|
-
this.aborted = aborted;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
Object.defineProperty(FetchTransportError, Symbol.for("dunx.deps"), {
|
|
42
|
-
value: () => [{ unresolved: "readonly response: { readonly method: string; readonly url: string }" }, { unresolved: "readonly aborted: boolean" }, ErrorOptions]
|
|
43
|
-
});
|
|
44
|
-
// src/client/json.ts
|
|
45
|
-
var safeStringify = (value) => {
|
|
46
|
-
const seen = new WeakSet;
|
|
47
|
-
return JSON.stringify(value, (_key, entry) => {
|
|
48
|
-
if (typeof entry === "object" && entry !== null) {
|
|
49
|
-
if (seen.has(entry))
|
|
50
|
-
return "[Circular]";
|
|
51
|
-
seen.add(entry);
|
|
52
|
-
}
|
|
53
|
-
return entry;
|
|
54
|
-
});
|
|
55
|
-
};
|
|
56
|
-
var isPlainObject = (value) => {
|
|
57
|
-
if (typeof value !== "object" || value === null)
|
|
58
|
-
return false;
|
|
59
|
-
const proto = Object.getPrototypeOf(value);
|
|
60
|
-
return proto === Object.prototype || proto === null;
|
|
61
|
-
};
|
|
62
|
-
var isJsonBody = (payload) => {
|
|
63
|
-
if (payload === null || payload === undefined)
|
|
64
|
-
return false;
|
|
65
|
-
if (typeof payload !== "object")
|
|
66
|
-
return typeof payload !== "string";
|
|
67
|
-
return !(payload instanceof FormData || payload instanceof URLSearchParams || payload instanceof Blob || payload instanceof ArrayBuffer || payload instanceof ReadableStream || ArrayBuffer.isView(payload));
|
|
68
|
-
};
|
|
3
|
+
TRACEPARENT_HEADER,
|
|
4
|
+
TraceContext
|
|
5
|
+
} from "./chunk-25g22350.js";
|
|
6
|
+
import {
|
|
7
|
+
FetchError,
|
|
8
|
+
FetchTransportError,
|
|
9
|
+
backoffDelay,
|
|
10
|
+
executeWithRetry,
|
|
11
|
+
isJsonBody,
|
|
12
|
+
isPlainObject,
|
|
13
|
+
isRetryableStatus,
|
|
14
|
+
retryAfterMs,
|
|
15
|
+
safeStringify
|
|
16
|
+
} from "./chunk-ywdpxbkf.js";
|
|
69
17
|
// src/client/options.ts
|
|
70
18
|
var DEFAULT_REQUEST_ID_HEADER = "x-request-id";
|
|
71
19
|
|
|
@@ -75,6 +23,7 @@ class HttpClientOptions {
|
|
|
75
23
|
headers;
|
|
76
24
|
retry;
|
|
77
25
|
requestIdHeader;
|
|
26
|
+
propagateTrace;
|
|
78
27
|
name;
|
|
79
28
|
fetchOptions;
|
|
80
29
|
constructor(init = {}) {
|
|
@@ -83,6 +32,7 @@ class HttpClientOptions {
|
|
|
83
32
|
this.headers = init.headers ?? {};
|
|
84
33
|
this.retry = init.retry ?? {};
|
|
85
34
|
this.name = init.name;
|
|
35
|
+
this.propagateTrace = init.propagateTrace ?? true;
|
|
86
36
|
const propagate = init.propagateRequestId ?? true;
|
|
87
37
|
this.requestIdHeader = propagate === false ? undefined : propagate === true ? DEFAULT_REQUEST_ID_HEADER : propagate;
|
|
88
38
|
this.fetchOptions = Object.fromEntries([
|
|
@@ -94,71 +44,7 @@ class HttpClientOptions {
|
|
|
94
44
|
].filter(([, value]) => value !== undefined));
|
|
95
45
|
}
|
|
96
46
|
}
|
|
97
|
-
Object.defineProperty(HttpClientOptions, Symbol.for("dunx.deps"), {
|
|
98
|
-
value: () => [{ unresolved: "init: HttpClientOptionsInit = {}" }]
|
|
99
|
-
});
|
|
100
|
-
// src/client/retry.ts
|
|
101
|
-
var uniform = () => {
|
|
102
|
-
const buffer = new Uint32Array(1);
|
|
103
|
-
crypto.getRandomValues(buffer);
|
|
104
|
-
return (buffer[0] ?? 0) / 2 ** 32;
|
|
105
|
-
};
|
|
106
|
-
var backoffDelay = (attempt, { baseMs, power = 2, jitterMs = 1000, maxMs = 30000 }) => Math.min(baseMs * power ** attempt + uniform() * jitterMs, maxMs);
|
|
107
|
-
var retryAfterMs = (headers, now = Date.now()) => {
|
|
108
|
-
const header = headers.get("retry-after");
|
|
109
|
-
if (header === null)
|
|
110
|
-
return;
|
|
111
|
-
const seconds = Number(header);
|
|
112
|
-
if (Number.isFinite(seconds))
|
|
113
|
-
return Math.max(0, seconds * 1000);
|
|
114
|
-
const at = Date.parse(header);
|
|
115
|
-
return Number.isNaN(at) ? undefined : Math.max(0, at - now);
|
|
116
|
-
};
|
|
117
|
-
var isRetryableStatus = (status) => status >= HttpStatusCode.INTERNAL_SERVER_ERROR || status === HttpStatusCode.REQUEST_TIMEOUT || status === HttpStatusCode.TOO_MANY_REQUESTS;
|
|
118
|
-
var decide = (error, attempt, options) => {
|
|
119
|
-
const {
|
|
120
|
-
retryDelayMs = 1000,
|
|
121
|
-
backoff,
|
|
122
|
-
shouldRetryOnStatus = isRetryableStatus,
|
|
123
|
-
respectRetryAfter = true
|
|
124
|
-
} = options;
|
|
125
|
-
const computed = backoffDelay(attempt, { baseMs: retryDelayMs, ...backoff });
|
|
126
|
-
if (error instanceof FetchTransportError) {
|
|
127
|
-
return { retry: !error.aborted, delayMs: computed };
|
|
128
|
-
}
|
|
129
|
-
if (error instanceof FetchError) {
|
|
130
|
-
if (!shouldRetryOnStatus(error.status))
|
|
131
|
-
return { retry: false, delayMs: 0 };
|
|
132
|
-
const asked = respectRetryAfter ? retryAfterMs(error.response.headers) : undefined;
|
|
133
|
-
const maxMs = backoff?.maxMs ?? 30000;
|
|
134
|
-
return {
|
|
135
|
-
retry: true,
|
|
136
|
-
delayMs: asked === undefined ? computed : Math.min(asked, maxMs)
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return { retry: true, delayMs: computed };
|
|
140
|
-
};
|
|
141
|
-
var executeWithRetry = async (operation, options = {}) => {
|
|
142
|
-
const { maxRetries = 3, onAttempt, onError, onSuccess } = options;
|
|
143
|
-
let lastError;
|
|
144
|
-
for (let attempt = 0;attempt <= maxRetries; attempt += 1) {
|
|
145
|
-
onAttempt?.(attempt + 1, attempt > 0);
|
|
146
|
-
try {
|
|
147
|
-
const result = await operation();
|
|
148
|
-
onSuccess?.(result, attempt + 1);
|
|
149
|
-
return result;
|
|
150
|
-
} catch (error) {
|
|
151
|
-
lastError = error;
|
|
152
|
-
const { retry, delayMs } = decide(error, attempt, options);
|
|
153
|
-
const willRetry = retry && attempt < maxRetries;
|
|
154
|
-
onError?.(error, attempt + 1, willRetry);
|
|
155
|
-
if (!willRetry)
|
|
156
|
-
throw error;
|
|
157
|
-
await Bun.sleep(delayMs);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
throw lastError;
|
|
161
|
-
};
|
|
47
|
+
Object.defineProperty(HttpClientOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: HttpClientOptionsInit = {}", optional: true }] });
|
|
162
48
|
// src/client/module.ts
|
|
163
49
|
import {
|
|
164
50
|
Logger as Logger2,
|
|
@@ -328,11 +214,19 @@ class HttpService extends UrlHelper {
|
|
|
328
214
|
}
|
|
329
215
|
async send(config, url, body, serialised, accept = "application/json") {
|
|
330
216
|
const requestId = this.options.requestIdHeader === undefined ? undefined : this.requestContext.getContext().requestId;
|
|
217
|
+
const trace = this.options.propagateTrace ? this.requestContext.getContext() : undefined;
|
|
331
218
|
const headers = {
|
|
332
219
|
accept,
|
|
333
220
|
...serialised === "" ? {} : { "content-type": "application/json" },
|
|
334
221
|
...this.options.headers,
|
|
335
222
|
...requestId === undefined || this.options.requestIdHeader === undefined ? {} : { [this.options.requestIdHeader]: requestId },
|
|
223
|
+
...typeof trace?.traceId === "string" && typeof trace.spanId === "string" ? {
|
|
224
|
+
[TRACEPARENT_HEADER]: TraceContext.header({
|
|
225
|
+
traceId: trace.traceId,
|
|
226
|
+
spanId: trace.spanId,
|
|
227
|
+
flags: "01"
|
|
228
|
+
})
|
|
229
|
+
} : {},
|
|
336
230
|
...config.headerFactory?.({
|
|
337
231
|
timestamp: Math.floor(Date.now() / 1000),
|
|
338
232
|
method: config.method,
|
|
@@ -360,9 +254,7 @@ class HttpService extends UrlHelper {
|
|
|
360
254
|
}
|
|
361
255
|
}
|
|
362
256
|
}
|
|
363
|
-
Object.defineProperty(HttpService, Symbol.for("dunx.deps"), {
|
|
364
|
-
value: () => [HttpClientOptions, Logger, RequestContext]
|
|
365
|
-
});
|
|
257
|
+
Object.defineProperty(HttpService, Symbol.for("dunx.deps"), { value: () => [HttpClientOptions, Logger, RequestContext] });
|
|
366
258
|
var urlOf = (url) => url === undefined ? {} : { url };
|
|
367
259
|
var readBody = async (response) => {
|
|
368
260
|
const text = await response.text().catch(() => "");
|
|
@@ -460,6 +352,3 @@ export {
|
|
|
460
352
|
retryAfterMs,
|
|
461
353
|
safeStringify
|
|
462
354
|
};
|
|
463
|
-
|
|
464
|
-
//# debugId=9448CCC7AF6FC82264756E2164756E21
|
|
465
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { BunRequest } from 'bun';
|
|
2
|
+
import type { RouteContext } from '../server/context.js';
|
|
3
|
+
import type { Middleware, Next } from '../server/middleware.js';
|
|
4
|
+
import { CompressionOptions } from './options.js';
|
|
5
|
+
/**
|
|
6
|
+
* Response compression, on Bun's own compressors. Not installed by default, and
|
|
7
|
+
* registered by the app rather than a module:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* const app = await HttpFactory.create(AppModule, { imports: [CompressionModule.forRoot()] });
|
|
11
|
+
* app.use(Compression);
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* An app that never registers it pays nothing. Position is the app's: compression
|
|
15
|
+
* belongs inside request logging and outside anything reading the body it made.
|
|
16
|
+
*
|
|
17
|
+
* Two encoders. A known length under `BUFFER_LIMIT` goes through the sync
|
|
18
|
+
* compressors, which keep an accurate `content-length`; anything larger or
|
|
19
|
+
* streamed goes through `CompressionStream` and loses the header.
|
|
20
|
+
*/
|
|
21
|
+
export declare class Compression implements Middleware {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(options: CompressionOptions);
|
|
24
|
+
handle(req: BunRequest, _ctx: RouteContext, next: Next): Promise<Response>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Deps, type DynamicModule, type FactoryProvider } from '@dunx/core';
|
|
2
|
+
import { type CompressionOptionsInit } from './options.js';
|
|
3
|
+
/**
|
|
4
|
+
* Binds `Compression` and its options. Like `StaticModule`, importing it does not
|
|
5
|
+
* install anything - the app decides where in the chain it goes:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* const app = await HttpFactory.create(AppModule, {
|
|
9
|
+
* imports: [CompressionModule.forRoot({ threshold: 2048 })],
|
|
10
|
+
* });
|
|
11
|
+
* app.use(Compression);
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare class CompressionModule {
|
|
15
|
+
static forRoot(init?: CompressionOptionsInit): DynamicModule;
|
|
16
|
+
/** `forRoot` with the options read off the container - a config value, usually. */
|
|
17
|
+
static forRootAsync<const D extends Deps>(config: FactoryProvider<CompressionOptionsInit, D> & {
|
|
18
|
+
readonly imports?: DynamicModule['imports'];
|
|
19
|
+
}): DynamicModule;
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CompressionEncoding } from './options.js';
|
|
2
|
+
/**
|
|
3
|
+
* The coding to encode with, or `undefined` for none.
|
|
4
|
+
*
|
|
5
|
+
* `offered` is the server's preference order and breaks a tie, which is what
|
|
6
|
+
* makes `['zstd', 'gzip']` meaningful against a browser that sends both at the
|
|
7
|
+
* same q. An explicit `q=0` refuses a coding, `*` supplies a default for the ones
|
|
8
|
+
* not named, and an absent header means the client said nothing - answered with
|
|
9
|
+
* no encoding, because a client that cannot decode is worse than one that reads
|
|
10
|
+
* a few more bytes.
|
|
11
|
+
*/
|
|
12
|
+
export declare const negotiate: (header: string | null, offered: readonly CompressionEncoding[]) => CompressionEncoding | undefined;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content codings this package produces.
|
|
3
|
+
*
|
|
4
|
+
* Brotli is absent on measurement: 6,344 us to encode a 6.4 KB JSON body against
|
|
5
|
+
* gzip's 23 us, and the `level` argument that would fix it is accepted and
|
|
6
|
+
* ignored. It belongs on a build artefact, not a per-request response.
|
|
7
|
+
*
|
|
8
|
+
* `deflate` is absent because Bun's two encoders disagree: `Bun.deflateSync` emits
|
|
9
|
+
* raw DEFLATE while `CompressionStream('deflate')` emits zlib, which is what the
|
|
10
|
+
* header is defined as. Nothing reconciles them, so offering it would flip wire
|
|
11
|
+
* format at the buffering threshold. Measured on Bun 1.4.0; see docs/bun-apis.md.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CompressionEncoding: Readonly<{
|
|
14
|
+
readonly ZSTD: 'zstd';
|
|
15
|
+
readonly GZIP: 'gzip';
|
|
16
|
+
}>;
|
|
17
|
+
export type CompressionEncoding = (typeof CompressionEncoding)[keyof typeof CompressionEncoding];
|
|
18
|
+
/**
|
|
19
|
+
* Whether a `content-type` is worth encoding.
|
|
20
|
+
*
|
|
21
|
+
* An already-compressed payload - a JPEG, an MP4, a zip - comes out of a second
|
|
22
|
+
* pass slightly larger, having spent the CPU to get there. The default answers
|
|
23
|
+
* no to anything it does not recognise, so a new binary type is skipped rather
|
|
24
|
+
* than wasted on.
|
|
25
|
+
*/
|
|
26
|
+
export declare const isCompressibleType: (contentType: string | null) => boolean;
|
|
27
|
+
export interface CompressionOptionsInit {
|
|
28
|
+
/**
|
|
29
|
+
* The codings offered, most preferred first. A tie in the client's q-values is
|
|
30
|
+
* broken by this order.
|
|
31
|
+
*
|
|
32
|
+
* `zstd` leads for speed: 7.7 us to 372 bytes on a 6.4 KB JSON body where gzip
|
|
33
|
+
* takes 16.1 us to reach 576. On a 116 KB document the sizes land within 0.2%,
|
|
34
|
+
* so the size advantage narrows with the body while the time one does not.
|
|
35
|
+
*
|
|
36
|
+
* @default ['zstd', 'gzip']
|
|
37
|
+
*/
|
|
38
|
+
readonly encodings?: readonly CompressionEncoding[];
|
|
39
|
+
/**
|
|
40
|
+
* Bodies below this many bytes are sent as they are.
|
|
41
|
+
*
|
|
42
|
+
* Only applied when the response declares a `content-length`. A short JSON body
|
|
43
|
+
* grows under gzip - the header and trailer alone are 18 bytes - and the round
|
|
44
|
+
* trip through a compressor is time spent to send more.
|
|
45
|
+
*
|
|
46
|
+
* @default 1024
|
|
47
|
+
*/
|
|
48
|
+
readonly threshold?: number;
|
|
49
|
+
/** Which `content-type`s to encode. @default isCompressibleType */
|
|
50
|
+
readonly filter?: (contentType: string | null) => boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A class, not an interface, so it is a runtime value `@dunx/transform` can record
|
|
54
|
+
* at an injection site - the same reason `StaticOptions` and `ThrottleOptions` are.
|
|
55
|
+
*/
|
|
56
|
+
export declare class CompressionOptions {
|
|
57
|
+
readonly encodings: readonly CompressionEncoding[];
|
|
58
|
+
readonly threshold: number;
|
|
59
|
+
readonly filter: (contentType: string | null) => boolean;
|
|
60
|
+
constructor(init?: CompressionOptionsInit);
|
|
61
|
+
}
|
|
@@ -22,17 +22,12 @@ export declare class MemoryOptions {
|
|
|
22
22
|
constructor(init: MemoryOptionsInit);
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
|
-
* Resident set size against a ceiling.
|
|
25
|
+
* Resident set size against a ceiling. `process.memoryUsage()` costs 5.96 us,
|
|
26
|
+
* which is what makes it safe on an endpoint scraped every two seconds;
|
|
27
|
+
* `jsc.heapStats()` is 2.2 ms and up and `v8.getHeapStatistics()` 1 to 7.6 ms.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* `jsc.heapStats()` walks every live object at 2.2 ms and up,
|
|
30
|
-
* `v8.getHeapStatistics()` is 1 to 7.6 ms, and `Bun.generateHeapSnapshot()` is
|
|
31
|
-
* hundreds of milliseconds and megabytes. None belongs on this path.
|
|
32
|
-
*
|
|
33
|
-
* Not critical: a process near its ceiling is worth seeing, and shedding traffic
|
|
34
|
-
* from it does not make it use less memory. Liveness is where a ceiling belongs, so
|
|
35
|
-
* the orchestrator restarts it.
|
|
29
|
+
* Not critical: shedding traffic from a process near its ceiling does not make it
|
|
30
|
+
* use less memory. A ceiling belongs on liveness, where it restarts.
|
|
36
31
|
*/
|
|
37
32
|
export declare class MemoryIndicator extends HealthIndicator {
|
|
38
33
|
private readonly options;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,44 +1,48 @@
|
|
|
1
1
|
export { Controller, Delete, Get, Patch, Post, Put, } from './route/decorators.js';
|
|
2
|
-
export {
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
5
|
-
export type { InferOutput, Input, JsonSchema, RouteInput, RouteSchemas, StandardSchemaIssue, StandardSchemaResult, StandardSchemaV1, } from './route/schema.js';
|
|
6
|
-
export { gatewaysOf, routesOf, type GatewayHandler, type GatewayNode, type RouteInputs, type RouteNode, } from './inspect.js';
|
|
2
|
+
export type { HttpMethod, RoutePath } from './route/marker.js';
|
|
3
|
+
export { ApiHidden, HIDDEN, meta, metaKey, metaOf, mergeMeta, Public, PUBLIC, Roles, ROLES, UNMATCHED, UseGuards, type MetaKey, type MetaRecord, } from './route/metadata.js';
|
|
4
|
+
export type { InferOutput, Input, JsonSchema, ResponseMap, Returns, RouteInput, RouteSchemas, StandardSchemaIssue, StandardSchemaResult, StandardSchemaV1, } from './route/schema.js';
|
|
7
5
|
export { ClientAddress } from './server/client-address.js';
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export { defaultErrorMapper, ErrorFilter, errorMapper, HttpError,
|
|
6
|
+
export type { RouteContext } from './server/context.js';
|
|
7
|
+
export type { CorsOptions, CorsOrigin } from './server/cors.js';
|
|
8
|
+
export { defaultErrorMapper, ErrorFilter, errorMapper, HttpError, ValidationError, type ErrorHandler, type ErrorMapper, type HttpErrorOptions, type InputSource, type ValidationIssue, } from './server/errors.js';
|
|
11
9
|
export { HttpFactory, type HttpApp, type HttpOptions, } from './server/factory.js';
|
|
12
10
|
export { REQUEST_ID_HEADER } from './server/request-id.js';
|
|
11
|
+
export { TRACEPARENT_HEADER, TRACESTATE_HEADER, TraceContext, type Trace, } from './server/trace-context.js';
|
|
13
12
|
export { RequestLoggingMiddleware, type RequestLoggingOptions, } from './server/request-logging.js';
|
|
14
|
-
export {
|
|
15
|
-
export { assertNoCollisions, assertNoGatewayCollisions, buildRoutes, withUpgradeRoutes, type BunRoutes, type GuardResolver, type RouteMethod, type ServeRoutes, } from './server/routes.js';
|
|
13
|
+
export type { Middleware, Next, RouteHandler } from './server/middleware.js';
|
|
16
14
|
export type { AppSettings } from './server/settings.js';
|
|
17
15
|
export { StaticFiles } from './static/files.js';
|
|
18
16
|
export { StaticModule } from './static/module.js';
|
|
19
|
-
export {
|
|
17
|
+
export { StaticOptions, type StaticOptionsInit } from './static/options.js';
|
|
18
|
+
export { Compression } from './compression/compression.js';
|
|
19
|
+
export { CompressionModule } from './compression/module.js';
|
|
20
|
+
export { CompressionEncoding, CompressionOptions, type CompressionOptionsInit, } from './compression/options.js';
|
|
20
21
|
export { SKIP_THROTTLE, SkipThrottle, THROTTLE, Throttle, type ThrottleLimit, } from './throttle/decorators.js';
|
|
21
22
|
export { ThrottleGuard } from './throttle/guard.js';
|
|
22
23
|
export { ThrottleModule } from './throttle/module.js';
|
|
23
24
|
export { ThrottleOptions, type ThrottleOptionsInit, } from './throttle/options.js';
|
|
24
25
|
export { MemoryThrottleStore, RedisThrottleStore, ThrottleStore, type ThrottleRedis, } from './throttle/store.js';
|
|
25
26
|
export { HttpStatusCode, type HttpStatusName } from './server/status.js';
|
|
26
|
-
export { buildWebSocket, type UpgradeHandler, type WebSocketRuntime, } from './ws/adapter.js';
|
|
27
27
|
export { Gateway, OnClose, OnDrain, OnMessage, OnOpen, OnPing, OnPong, OnUpgrade, } from './ws/decorators.js';
|
|
28
|
-
export
|
|
29
|
-
export {
|
|
30
|
-
export { composeSocket, observe, type SocketContext, type SocketDispatch, type SocketFrame, type SocketMiddleware, type SocketNext, } from './ws/middleware.js';
|
|
28
|
+
export type { Envelope } from './ws/envelope.js';
|
|
29
|
+
export type { SocketContext, SocketDispatch, SocketFrame, SocketMiddleware, SocketNext, } from './ws/middleware.js';
|
|
31
30
|
export { SocketLoggingMiddleware, type SocketLoggingOptions, } from './ws/logging.js';
|
|
32
|
-
export { HandlerKind, isGateway, type HandlerMeta } from './ws/marker.js';
|
|
33
31
|
export { PubSub } from './ws/pubsub.js';
|
|
34
|
-
export {
|
|
35
|
-
export {
|
|
36
|
-
export { buildGateways, buildRuntime, type GatewayRuntime, } from './ws/runtime.js';
|
|
32
|
+
export { RedisRelay, type RedisRelayOptions } from './ws/redis-relay.js';
|
|
33
|
+
export { DEFAULT_RELAY_CHANNEL, type PubSubRelay, type RelayOptions, } from './ws/relay.js';
|
|
37
34
|
export type { Socket, SocketData, SocketErrorHandler, SocketOptions, } from './ws/socket.js';
|
|
38
35
|
export { HealthIndicator, PingProbe, QueryProbe, type ProbeResult, type ProbeState, } from './health/contracts.js';
|
|
39
|
-
export { HealthController
|
|
36
|
+
export { HealthController } from './health/controller.js';
|
|
40
37
|
export { DatabaseIndicator, DiskIndicator, DiskOptions, MemoryIndicator, MemoryOptions, RedisIndicator, type DiskOptionsInit, type MemoryOptionsInit, } from './health/indicators.js';
|
|
41
38
|
export { HealthModule } from './health/module.js';
|
|
42
39
|
export { HEALTH_REPORT_SCHEMA } from './health/report-schema.js';
|
|
43
40
|
export { Readiness, ReadinessOptions } from './health/readiness.js';
|
|
44
41
|
export { HealthOptions, HealthRegistry, type HealthCheckReport, type HealthOptionsInit, type HealthReport, } from './health/registry.js';
|
|
42
|
+
/**
|
|
43
|
+
* The framework's own plumbing, still reachable here and moving out in 4.0.
|
|
44
|
+
* Import it from `@dunx/http/internal`, which carries no stability promise.
|
|
45
|
+
*
|
|
46
|
+
* @deprecated Import from `@dunx/http/internal`. Removed in 4.0.
|
|
47
|
+
*/
|
|
48
|
+
export { assertNoCollisions, assertNoGatewayCollisions, buildContext, buildGateways, buildRoutes, buildRuntime, buildWebSocket, compose, composeSocket, decode, decodeRelay, defaultRelayUrl, defaultStatusFor, discoverGateway, discoverGateways, discoverRoutes, encode, encodeRelay, gatewaysOf, guardsOf, HandlerKind, HiddenHealthController, isCompressibleType, isErrorFilter, isGateway, joinPath, negotiate, normalizePath, normalizePrefix, observe, preflight, routesOf, toErrorMapper, withCors, withUpgradeRoutes, type BunRoutes, type DefaultStatus, type DiscoveredGateway, type DiscoveredHandler, type DiscoveredRoute, type GatewayHandler, type GatewayNode, type GatewayRuntime, type GuardResolver, type HandlerMeta, type Invoke, type RelayFrame, type RelayPhase, type RouteInputs, type RouteMeta, type RouteMethod, type RouteNode, type ServeRoutes, type UpgradeHandler, type WebSocketRuntime, } from './internal.js';
|