@dunx/http 3.2.0 → 3.3.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-8939brh2.js +174 -0
- package/dist/chunk-bg0dr54z.js +37 -0
- package/dist/chunk-gmtwad7f.js +71 -0
- package/dist/client.js +128 -12
- package/dist/index.js +1411 -155
- package/dist/inspect.d.ts +9 -0
- package/dist/internal.d.ts +8 -25
- package/dist/internal.js +27 -99
- package/dist/server/application.d.ts +7 -99
- package/dist/server/binding.d.ts +67 -0
- package/dist/server/factory.d.ts +4 -2
- package/dist/server/html.d.ts +10 -0
- package/dist/server/metrics.d.ts +8 -3
- package/dist/server/options-provider.d.ts +14 -1
- package/dist/server/options.d.ts +141 -0
- package/dist/ws/decorators.d.ts +1 -1
- package/dist/ws/postgres-relay.d.ts +0 -2
- package/dist/ws/relay.d.ts +8 -0
- package/dist/ws/socket.d.ts +16 -0
- package/package.json +4 -4
- package/dist/chunk-9x3evk19.js +0 -177
- package/dist/chunk-e8a9c6j2.js +0 -130
- package/dist/chunk-y85wcdhw.js +0 -1297
package/dist/chunk-y85wcdhw.js
DELETED
|
@@ -1,1297 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
HttpStatusCode,
|
|
4
|
-
TraceContext,
|
|
5
|
-
__decorateElement,
|
|
6
|
-
__decoratorMetadata,
|
|
7
|
-
__decoratorStart,
|
|
8
|
-
__privateAdd,
|
|
9
|
-
__privateGet,
|
|
10
|
-
__runInitializers
|
|
11
|
-
} from "./chunk-9x3evk19.js";
|
|
12
|
-
|
|
13
|
-
// src/route/marker.ts
|
|
14
|
-
var ROUTE = Symbol.for("dunx.route");
|
|
15
|
-
var CONTROLLER = Symbol.for("dunx.controller");
|
|
16
|
-
var defaultStatusFor = (method) => method === "POST" ? HttpStatusCode.CREATED : HttpStatusCode.OK;
|
|
17
|
-
var resolvePath = (path) => typeof path === "function" ? path() : path;
|
|
18
|
-
var markRoute = (target, meta) => {
|
|
19
|
-
Object.defineProperty(target, ROUTE, { value: meta, configurable: true });
|
|
20
|
-
};
|
|
21
|
-
var routeMetaOf = (value) => typeof value === "function" ? value[ROUTE] : undefined;
|
|
22
|
-
var markController = (target, prefix) => {
|
|
23
|
-
Object.defineProperty(target, CONTROLLER, {
|
|
24
|
-
value: prefix,
|
|
25
|
-
configurable: true
|
|
26
|
-
});
|
|
27
|
-
};
|
|
28
|
-
var prefixOf = (target) => target[CONTROLLER] ?? "";
|
|
29
|
-
|
|
30
|
-
// src/route/decorators.ts
|
|
31
|
-
var Controller = (prefix = "") => (target) => {
|
|
32
|
-
markController(target, prefix);
|
|
33
|
-
return target;
|
|
34
|
-
};
|
|
35
|
-
var verb = (method) => (path = "/", options) => (value, _context) => {
|
|
36
|
-
markRoute(value, { method, path, options });
|
|
37
|
-
return value;
|
|
38
|
-
};
|
|
39
|
-
var Get = verb("GET");
|
|
40
|
-
var Post = verb("POST");
|
|
41
|
-
var Put = verb("PUT");
|
|
42
|
-
var Patch = verb("PATCH");
|
|
43
|
-
var Delete = verb("DELETE");
|
|
44
|
-
|
|
45
|
-
// src/route/metadata.ts
|
|
46
|
-
var META = Symbol.for("dunx.meta");
|
|
47
|
-
var GUARDS = Symbol.for("dunx.guards");
|
|
48
|
-
var metaKey = (name) => ({
|
|
49
|
-
name,
|
|
50
|
-
id: Symbol(name)
|
|
51
|
-
});
|
|
52
|
-
var write = (target, key, value) => {
|
|
53
|
-
const record = new Map(target[META]);
|
|
54
|
-
record.set(key.id, value);
|
|
55
|
-
Object.defineProperty(target, META, { value: record, configurable: true });
|
|
56
|
-
};
|
|
57
|
-
var meta = (key, value) => (target) => {
|
|
58
|
-
write(target, key, value);
|
|
59
|
-
return target;
|
|
60
|
-
};
|
|
61
|
-
var ROLES = metaKey("roles");
|
|
62
|
-
var PUBLIC = metaKey("public");
|
|
63
|
-
var HIDDEN = metaKey("hidden");
|
|
64
|
-
var UNMATCHED = metaKey("unmatched");
|
|
65
|
-
var Roles = (...roles) => meta(ROLES, roles);
|
|
66
|
-
var Public = () => meta(PUBLIC, true);
|
|
67
|
-
var ApiHidden = () => meta(HIDDEN, true);
|
|
68
|
-
var UseGuards = (...guards) => (target) => {
|
|
69
|
-
const existing = target[GUARDS] ?? [];
|
|
70
|
-
const merged = Object.hasOwn(target, GUARDS) ? [...guards, ...existing] : [...existing, ...guards];
|
|
71
|
-
Object.defineProperty(target, GUARDS, {
|
|
72
|
-
value: merged,
|
|
73
|
-
configurable: true
|
|
74
|
-
});
|
|
75
|
-
return target;
|
|
76
|
-
};
|
|
77
|
-
var guardsOf = (target) => target[GUARDS] ?? [];
|
|
78
|
-
var metaOf = (target) => target[META];
|
|
79
|
-
var mergeMeta = (...targets) => {
|
|
80
|
-
const merged = new Map;
|
|
81
|
-
for (const target of targets) {
|
|
82
|
-
const record = target[META];
|
|
83
|
-
if (record)
|
|
84
|
-
for (const [id, value] of record)
|
|
85
|
-
merged.set(id, value);
|
|
86
|
-
}
|
|
87
|
-
return merged;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
// src/server/errors.ts
|
|
91
|
-
import { AppError, ConsoleLogger } from "@dunx/core";
|
|
92
|
-
class HttpError extends AppError {
|
|
93
|
-
status;
|
|
94
|
-
name = "HttpError";
|
|
95
|
-
headers;
|
|
96
|
-
constructor(status, message, options) {
|
|
97
|
-
super(message, options);
|
|
98
|
-
this.status = status;
|
|
99
|
-
this.headers = options?.headers;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
Object.defineProperty(HttpError, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "override readonly status: number" }, { unresolved: "message: string" }, { unresolved: "options?: HttpErrorOptions" }] });
|
|
103
|
-
|
|
104
|
-
class ValidationError extends HttpError {
|
|
105
|
-
source;
|
|
106
|
-
issues;
|
|
107
|
-
name = "ValidationError";
|
|
108
|
-
constructor(source, issues) {
|
|
109
|
-
super(HttpStatusCode.BAD_REQUEST, `Invalid ${source}`);
|
|
110
|
-
this.source = source;
|
|
111
|
-
this.issues = issues;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
Object.defineProperty(ValidationError, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "readonly source: InputSource" }, { unresolved: "readonly issues: readonly ValidationIssue[]" }] });
|
|
115
|
-
|
|
116
|
-
class ErrorFilter {
|
|
117
|
-
}
|
|
118
|
-
var isErrorFilter = (handler) => typeof handler === "function" && typeof handler.prototype?.catch === "function";
|
|
119
|
-
var toErrorMapper = (handler, resolve) => isErrorFilter(handler) ? (error, req) => resolve(handler).catch(error, req) : handler;
|
|
120
|
-
var errorMapper = (logger) => (error) => {
|
|
121
|
-
if (error instanceof ValidationError) {
|
|
122
|
-
return Response.json({ error: error.message, status: error.status, issues: error.issues }, {
|
|
123
|
-
status: error.status,
|
|
124
|
-
...error.headers && { headers: error.headers }
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (error instanceof HttpError) {
|
|
128
|
-
return Response.json({ error: error.message, status: error.status }, {
|
|
129
|
-
status: error.status,
|
|
130
|
-
...error.headers && { headers: error.headers }
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
if (error instanceof AppError && isStatus(error.status)) {
|
|
134
|
-
if (error.status >= HttpStatusCode.INTERNAL_SERVER_ERROR) {
|
|
135
|
-
logger.error("Unhandled error", error);
|
|
136
|
-
}
|
|
137
|
-
return Response.json({ error: error.message, status: error.status }, { status: error.status });
|
|
138
|
-
}
|
|
139
|
-
logger.error("Unhandled error", error);
|
|
140
|
-
return Response.json({
|
|
141
|
-
error: "Internal Server Error",
|
|
142
|
-
status: HttpStatusCode.INTERNAL_SERVER_ERROR
|
|
143
|
-
}, { status: HttpStatusCode.INTERNAL_SERVER_ERROR });
|
|
144
|
-
};
|
|
145
|
-
var isStatus = (value) => value !== undefined && Number.isInteger(value) && value >= 200 && value <= 599;
|
|
146
|
-
var defaultErrorMapper = errorMapper(new ConsoleLogger);
|
|
147
|
-
|
|
148
|
-
// src/route/discover.ts
|
|
149
|
-
import { markedMethods } from "@dunx/core";
|
|
150
|
-
var joinPath = (prefix, path) => {
|
|
151
|
-
const joined = `/${prefix}/${path}`.replace(/\/{2,}/g, "/");
|
|
152
|
-
return joined.length > 1 ? joined.replace(/\/$/, "") : "/";
|
|
153
|
-
};
|
|
154
|
-
var discoverRoutes = (instance) => {
|
|
155
|
-
const klass = instance.constructor;
|
|
156
|
-
const prefix = prefixOf(klass);
|
|
157
|
-
const classGuards = guardsOf(klass);
|
|
158
|
-
const members = instance;
|
|
159
|
-
return markedMethods(Object.getPrototypeOf(instance), routeMetaOf).map(({ name, meta: meta2, value: marked }) => ({
|
|
160
|
-
method: meta2.method,
|
|
161
|
-
path: joinPath(prefix, resolvePath(meta2.path)),
|
|
162
|
-
controller: klass.name,
|
|
163
|
-
handlerName: name,
|
|
164
|
-
handler: members[name].bind(instance),
|
|
165
|
-
options: meta2.options,
|
|
166
|
-
meta: mergeMeta(klass, marked),
|
|
167
|
-
classMeta: metaOf(klass),
|
|
168
|
-
guards: [...classGuards, ...guardsOf(marked)]
|
|
169
|
-
}));
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// src/ws/envelope.ts
|
|
173
|
-
var encode = (event, data) => JSON.stringify({ event, data });
|
|
174
|
-
var decode = (message) => {
|
|
175
|
-
if (typeof message !== "string")
|
|
176
|
-
return;
|
|
177
|
-
let parsed;
|
|
178
|
-
try {
|
|
179
|
-
parsed = JSON.parse(message);
|
|
180
|
-
} catch {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (typeof parsed !== "object" || parsed === null)
|
|
184
|
-
return;
|
|
185
|
-
const { event, data } = parsed;
|
|
186
|
-
return typeof event === "string" ? { event, data } : undefined;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// src/ws/marker.ts
|
|
190
|
-
var HANDLER = Symbol.for("dunx.ws.handler");
|
|
191
|
-
var GATEWAY = Symbol.for("dunx.ws.gateway");
|
|
192
|
-
var HandlerKind = Object.freeze({
|
|
193
|
-
UPGRADE: "upgrade",
|
|
194
|
-
OPEN: "open",
|
|
195
|
-
MESSAGE: "message",
|
|
196
|
-
CLOSE: "close",
|
|
197
|
-
DRAIN: "drain",
|
|
198
|
-
PING: "ping",
|
|
199
|
-
PONG: "pong"
|
|
200
|
-
});
|
|
201
|
-
var markHandler = (target, meta2) => {
|
|
202
|
-
Object.defineProperty(target, HANDLER, { value: meta2, configurable: true });
|
|
203
|
-
};
|
|
204
|
-
var handlerMetaOf = (value) => typeof value === "function" ? value[HANDLER] : undefined;
|
|
205
|
-
var markGateway = (target, path) => {
|
|
206
|
-
Object.defineProperty(target, GATEWAY, { value: path, configurable: true });
|
|
207
|
-
};
|
|
208
|
-
var gatewayPathOf = (target) => target[GATEWAY] ?? "/";
|
|
209
|
-
var isGateway = (target) => target[GATEWAY] !== undefined;
|
|
210
|
-
|
|
211
|
-
// src/ws/middleware.ts
|
|
212
|
-
var composeSocket = (middleware, ctx) => middleware.reduceRight((next, current) => (frame, run) => current.handle(frame, ctx, () => next(frame, run)), (_frame, run) => run());
|
|
213
|
-
var observe = (next, done) => {
|
|
214
|
-
let result;
|
|
215
|
-
try {
|
|
216
|
-
result = next();
|
|
217
|
-
} catch (error) {
|
|
218
|
-
done(error, undefined);
|
|
219
|
-
throw error;
|
|
220
|
-
}
|
|
221
|
-
if (result instanceof Promise) {
|
|
222
|
-
return result.then((value) => {
|
|
223
|
-
done(undefined, value);
|
|
224
|
-
return value;
|
|
225
|
-
}, (error) => {
|
|
226
|
-
done(error, undefined);
|
|
227
|
-
throw error;
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
done(undefined, result);
|
|
231
|
-
return result;
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
// src/ws/runtime.ts
|
|
235
|
-
import { AppError as AppError2 } from "@dunx/core";
|
|
236
|
-
var slotOf = (handler) => handler.kind === HandlerKind.MESSAGE && handler.event !== undefined ? `message ${JSON.stringify(handler.event)}` : handler.kind;
|
|
237
|
-
var buildRuntime = (gateway) => {
|
|
238
|
-
if (gateway.handlers.length === 0) {
|
|
239
|
-
throw new AppError2(`${gateway.name} is registered as a gateway but declares no handlers. ` + "Add an @OnMessage/@OnOpen/... method, or drop the @Gateway decorator.");
|
|
240
|
-
}
|
|
241
|
-
const owners = new Map;
|
|
242
|
-
const events = new Map;
|
|
243
|
-
for (const handler of gateway.handlers) {
|
|
244
|
-
const slot = slotOf(handler);
|
|
245
|
-
const existing = owners.get(slot);
|
|
246
|
-
if (existing) {
|
|
247
|
-
throw new AppError2(`Handler collision in ${gateway.name}: ${slot} is claimed by ` + `${existing.method}() and by ${handler.method}(). One handler per event.`);
|
|
248
|
-
}
|
|
249
|
-
owners.set(slot, handler);
|
|
250
|
-
if (handler.kind === HandlerKind.MESSAGE && handler.event !== undefined) {
|
|
251
|
-
events.set(handler.event, handler.invoke);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
const at = (slot) => owners.get(slot)?.invoke;
|
|
255
|
-
return {
|
|
256
|
-
name: gateway.name,
|
|
257
|
-
path: gateway.path,
|
|
258
|
-
upgrade: at(HandlerKind.UPGRADE),
|
|
259
|
-
open: at(HandlerKind.OPEN),
|
|
260
|
-
close: at(HandlerKind.CLOSE),
|
|
261
|
-
drain: at(HandlerKind.DRAIN),
|
|
262
|
-
ping: at(HandlerKind.PING),
|
|
263
|
-
pong: at(HandlerKind.PONG),
|
|
264
|
-
raw: at(HandlerKind.MESSAGE),
|
|
265
|
-
events
|
|
266
|
-
};
|
|
267
|
-
};
|
|
268
|
-
var buildGateways = (discovered) => {
|
|
269
|
-
const byPath = new Map;
|
|
270
|
-
for (const gateway of discovered) {
|
|
271
|
-
const existing = byPath.get(gateway.path);
|
|
272
|
-
if (existing) {
|
|
273
|
-
throw new AppError2(`Gateway path collision: ${gateway.path} is served by ${existing.name} ` + `and by ${gateway.name}. One gateway per path.`);
|
|
274
|
-
}
|
|
275
|
-
byPath.set(gateway.path, buildRuntime(gateway));
|
|
276
|
-
}
|
|
277
|
-
return byPath;
|
|
278
|
-
};
|
|
279
|
-
var someHandler = (gateways, pick) => {
|
|
280
|
-
for (const gateway of gateways)
|
|
281
|
-
if (pick(gateway) !== undefined)
|
|
282
|
-
return true;
|
|
283
|
-
return false;
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// src/ws/adapter.ts
|
|
287
|
-
var RUNTIME = Symbol.for("dunx.ws.runtime");
|
|
288
|
-
var UNCLAIMED = Symbol.for("dunx.ws.unclaimed");
|
|
289
|
-
var defaultOnError = (error, socket) => {
|
|
290
|
-
console.error(`[dunx/http] ${socket.data.path} handler failed:`, error);
|
|
291
|
-
};
|
|
292
|
-
var reportedByMiddleware = () => {
|
|
293
|
-
return;
|
|
294
|
-
};
|
|
295
|
-
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(", ")}.`;
|
|
296
|
-
var runtimeOf = (socket) => socket.data[RUNTIME];
|
|
297
|
-
var isBinary = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
|
|
298
|
-
var replyRaw = (socket, value) => {
|
|
299
|
-
if (value === undefined)
|
|
300
|
-
return;
|
|
301
|
-
socket.send(typeof value === "string" || isBinary(value) ? value : JSON.stringify(value));
|
|
302
|
-
};
|
|
303
|
-
var settle = (result, socket, onError, then) => {
|
|
304
|
-
if (result instanceof Promise) {
|
|
305
|
-
result.then((value) => {
|
|
306
|
-
if (!then)
|
|
307
|
-
return;
|
|
308
|
-
try {
|
|
309
|
-
then(value);
|
|
310
|
-
} catch (error) {
|
|
311
|
-
onError(error, socket);
|
|
312
|
-
}
|
|
313
|
-
}, (error) => onError(error, socket));
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
if (then)
|
|
317
|
-
then(result);
|
|
318
|
-
};
|
|
319
|
-
var framing = (kind) => {
|
|
320
|
-
if (kind === HandlerKind.CLOSE) {
|
|
321
|
-
return (args) => ({
|
|
322
|
-
socket: args[0],
|
|
323
|
-
data: { code: args[1], reason: args[2] }
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
if (kind === HandlerKind.OPEN || kind === HandlerKind.DRAIN) {
|
|
327
|
-
return (args) => ({ socket: args[0], data: undefined });
|
|
328
|
-
}
|
|
329
|
-
return (args) => ({ socket: args[1], data: args[0] });
|
|
330
|
-
};
|
|
331
|
-
var NOTHING = () => {
|
|
332
|
-
return;
|
|
333
|
-
};
|
|
334
|
-
var through = (gateway, middleware, kind, event, invoke) => {
|
|
335
|
-
const ctx = {
|
|
336
|
-
gateway: gateway.name,
|
|
337
|
-
path: gateway.path,
|
|
338
|
-
kind,
|
|
339
|
-
event
|
|
340
|
-
};
|
|
341
|
-
const dispatch = composeSocket(middleware, ctx);
|
|
342
|
-
const frameOf = framing(kind);
|
|
343
|
-
const run = invoke ?? NOTHING;
|
|
344
|
-
return (...args) => dispatch(frameOf(args), () => run(...args));
|
|
345
|
-
};
|
|
346
|
-
var withMiddleware = (gateway, middleware) => {
|
|
347
|
-
const wrap = (kind, event, invoke) => through(gateway, middleware, kind, event, invoke);
|
|
348
|
-
const optional = (kind, invoke) => invoke === undefined ? undefined : wrap(kind, undefined, invoke);
|
|
349
|
-
return {
|
|
350
|
-
...gateway,
|
|
351
|
-
open: wrap(HandlerKind.OPEN, undefined, gateway.open),
|
|
352
|
-
close: wrap(HandlerKind.CLOSE, undefined, gateway.close),
|
|
353
|
-
drain: optional(HandlerKind.DRAIN, gateway.drain),
|
|
354
|
-
ping: optional(HandlerKind.PING, gateway.ping),
|
|
355
|
-
pong: optional(HandlerKind.PONG, gateway.pong),
|
|
356
|
-
raw: optional(HandlerKind.MESSAGE, gateway.raw),
|
|
357
|
-
events: new Map([...gateway.events].map(([event, invoke]) => [
|
|
358
|
-
event,
|
|
359
|
-
wrap(HandlerKind.MESSAGE, event, invoke)
|
|
360
|
-
]))
|
|
361
|
-
};
|
|
362
|
-
};
|
|
363
|
-
var unclaimedDispatch = (gateway, middleware) => (frame, event) => composeSocket(middleware, {
|
|
364
|
-
gateway: gateway.name,
|
|
365
|
-
path: gateway.path,
|
|
366
|
-
kind: HandlerKind.MESSAGE,
|
|
367
|
-
event
|
|
368
|
-
})(frame, () => {
|
|
369
|
-
return;
|
|
370
|
-
});
|
|
371
|
-
var buildWebSocket = (discovered, options = {}, middleware = []) => {
|
|
372
|
-
const byPath = buildGateways(discovered);
|
|
373
|
-
const wrapped = middleware.length === 0 ? byPath : new Map([...byPath].map(([path, gateway]) => [
|
|
374
|
-
path,
|
|
375
|
-
withMiddleware(gateway, middleware)
|
|
376
|
-
]));
|
|
377
|
-
const gateways = [...wrapped.values()];
|
|
378
|
-
const onError = options.onError ?? (middleware.length === 0 ? defaultOnError : reportedByMiddleware);
|
|
379
|
-
const reports = options.onError !== undefined || middleware.some((entry) => entry.reportsErrors === true);
|
|
380
|
-
const { onError: _onError, ...socketOptions } = options;
|
|
381
|
-
const run = (invoke, args, ws, then) => {
|
|
382
|
-
try {
|
|
383
|
-
settle(invoke(...args), ws, onError, then);
|
|
384
|
-
} catch (error) {
|
|
385
|
-
onError(error, ws);
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
const websocket = {
|
|
389
|
-
...socketOptions,
|
|
390
|
-
message(ws, message) {
|
|
391
|
-
const gateway = runtimeOf(ws);
|
|
392
|
-
let event;
|
|
393
|
-
if (gateway.events.size > 0) {
|
|
394
|
-
const envelope = decode(message);
|
|
395
|
-
const handler = envelope && gateway.events.get(envelope.event);
|
|
396
|
-
if (envelope && handler) {
|
|
397
|
-
run(handler, [envelope.data, ws], ws, (value) => {
|
|
398
|
-
if (value !== undefined)
|
|
399
|
-
ws.send(encode(envelope.event, value));
|
|
400
|
-
});
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
event = envelope?.event;
|
|
404
|
-
}
|
|
405
|
-
if (gateway.raw) {
|
|
406
|
-
run(gateway.raw, [message, ws], ws, (value) => replyRaw(ws, value));
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const unclaimed2 = ws.data[UNCLAIMED];
|
|
410
|
-
if (!unclaimed2)
|
|
411
|
-
return;
|
|
412
|
-
try {
|
|
413
|
-
settle(unclaimed2({ socket: ws, data: message }, event), ws, onError, undefined);
|
|
414
|
-
} catch (error) {
|
|
415
|
-
onError(error, ws);
|
|
416
|
-
}
|
|
417
|
-
},
|
|
418
|
-
...someHandler(gateways, (g) => g.open) && {
|
|
419
|
-
open(ws) {
|
|
420
|
-
const { open } = runtimeOf(ws);
|
|
421
|
-
if (open)
|
|
422
|
-
run(open, [ws], ws, undefined);
|
|
423
|
-
}
|
|
424
|
-
},
|
|
425
|
-
...someHandler(gateways, (g) => g.close) && {
|
|
426
|
-
close(ws, code, reason) {
|
|
427
|
-
const { close } = runtimeOf(ws);
|
|
428
|
-
if (close)
|
|
429
|
-
run(close, [ws, code, reason], ws, undefined);
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
...someHandler(gateways, (g) => g.drain) && {
|
|
433
|
-
drain(ws) {
|
|
434
|
-
const { drain } = runtimeOf(ws);
|
|
435
|
-
if (drain)
|
|
436
|
-
run(drain, [ws], ws, undefined);
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
...someHandler(gateways, (g) => g.ping) && {
|
|
440
|
-
ping(ws, data) {
|
|
441
|
-
const { ping } = runtimeOf(ws);
|
|
442
|
-
if (ping)
|
|
443
|
-
run(ping, [data, ws], ws, undefined);
|
|
444
|
-
}
|
|
445
|
-
},
|
|
446
|
-
...someHandler(gateways, (g) => g.pong) && {
|
|
447
|
-
pong(ws, data) {
|
|
448
|
-
const { pong } = runtimeOf(ws);
|
|
449
|
-
if (pong)
|
|
450
|
-
run(pong, [data, ws], ws, undefined);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
const unclaimed = new Map(middleware.length === 0 ? [] : gateways.map((gateway) => [
|
|
455
|
-
gateway,
|
|
456
|
-
unclaimedDispatch(gateway, middleware)
|
|
457
|
-
]));
|
|
458
|
-
const accept = (req, server, gateway, context) => {
|
|
459
|
-
const fallback = unclaimed.get(gateway);
|
|
460
|
-
const data = {
|
|
461
|
-
path: gateway.path,
|
|
462
|
-
context,
|
|
463
|
-
id: crypto.randomUUID(),
|
|
464
|
-
[RUNTIME]: gateway,
|
|
465
|
-
...fallback === undefined ? {} : { [UNCLAIMED]: fallback }
|
|
466
|
-
};
|
|
467
|
-
return server.upgrade(req, { data }) ? undefined : new Response("Expected a WebSocket upgrade", { status: 426 });
|
|
468
|
-
};
|
|
469
|
-
const upgradeHandler = (gateway) => (req, server) => {
|
|
470
|
-
if (!gateway.upgrade)
|
|
471
|
-
return accept(req, server, gateway, undefined);
|
|
472
|
-
const result = gateway.upgrade(req);
|
|
473
|
-
if (result instanceof Promise) {
|
|
474
|
-
return result.then((value) => value instanceof Response ? value : accept(req, server, gateway, value));
|
|
475
|
-
}
|
|
476
|
-
return result instanceof Response ? result : accept(req, server, gateway, result);
|
|
477
|
-
};
|
|
478
|
-
return {
|
|
479
|
-
websocket,
|
|
480
|
-
routes: new Map(gateways.map((gateway) => [gateway.path, upgradeHandler(gateway)])),
|
|
481
|
-
paths: [...byPath.keys()],
|
|
482
|
-
warnings: middleware.length > 0 && !reports ? [unreported(middleware)] : [],
|
|
483
|
-
gateways: gateways.map((gateway) => ({
|
|
484
|
-
name: gateway.name,
|
|
485
|
-
path: gateway.path,
|
|
486
|
-
events: [...gateway.events.keys()]
|
|
487
|
-
}))
|
|
488
|
-
};
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
// src/ws/discover.ts
|
|
492
|
-
import {
|
|
493
|
-
AppError as AppError3,
|
|
494
|
-
classOf,
|
|
495
|
-
markedMethods as markedMethods2
|
|
496
|
-
} from "@dunx/core";
|
|
497
|
-
var normalizePath = (path) => {
|
|
498
|
-
const joined = `/${path}`.replace(/\/{2,}/g, "/");
|
|
499
|
-
return joined.length > 1 ? joined.replace(/\/$/, "") : "/";
|
|
500
|
-
};
|
|
501
|
-
var eachHandler = (start) => markedMethods2(start, handlerMetaOf);
|
|
502
|
-
var discoverGateway = (instance) => {
|
|
503
|
-
const klass = instance.constructor;
|
|
504
|
-
const members = instance;
|
|
505
|
-
return {
|
|
506
|
-
name: klass.name,
|
|
507
|
-
path: normalizePath(gatewayPathOf(klass)),
|
|
508
|
-
handlers: eachHandler(Object.getPrototypeOf(instance)).map(({ name, meta: meta2 }) => ({
|
|
509
|
-
kind: meta2.kind,
|
|
510
|
-
event: meta2.event,
|
|
511
|
-
method: name,
|
|
512
|
-
invoke: members[name].bind(instance)
|
|
513
|
-
}))
|
|
514
|
-
};
|
|
515
|
-
};
|
|
516
|
-
var findHandlerMethod = (ctor) => eachHandler(ctor.prototype)[0]?.name;
|
|
517
|
-
var discoverGateways = (modules, resolve) => {
|
|
518
|
-
const discovered = [];
|
|
519
|
-
for (const module of modules) {
|
|
520
|
-
for (const entry of module.options.providers ?? []) {
|
|
521
|
-
const candidate = classOf(entry);
|
|
522
|
-
if (!candidate)
|
|
523
|
-
continue;
|
|
524
|
-
if (isGateway(candidate.ctor)) {
|
|
525
|
-
discovered.push(discoverGateway(resolve(candidate.token)));
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
const orphan = findHandlerMethod(candidate.ctor);
|
|
529
|
-
if (orphan !== undefined) {
|
|
530
|
-
throw new AppError3(`${candidate.ctor.name}.${orphan}() is a websocket handler, but ` + `${candidate.ctor.name} is not a gateway. Decorate the class with ` + "@Gateway(path), or drop the handler decorator.");
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
return discovered;
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// src/ws/relay.ts
|
|
538
|
-
import { AppError as AppError4 } from "@dunx/core";
|
|
539
|
-
var DEFAULT_RELAY_CHANNEL = "dunx:ws";
|
|
540
|
-
var defaultRelayError = (error, phase) => {
|
|
541
|
-
console.warn(`[dunx/http] the websocket relay could not ${phase}. Fan-out is local to ` + "this process until it recovers:", error);
|
|
542
|
-
};
|
|
543
|
-
var toBytes = (data) => ArrayBuffer.isView(data) ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength) : new Uint8Array(data);
|
|
544
|
-
var encodeRelay = (origin, topic, data) => typeof data === "string" ? JSON.stringify({ o: origin, t: topic, d: data }) : JSON.stringify({
|
|
545
|
-
o: origin,
|
|
546
|
-
t: topic,
|
|
547
|
-
d: Buffer.from(toBytes(data)).toString("base64"),
|
|
548
|
-
b: 1
|
|
549
|
-
});
|
|
550
|
-
var decodeRelay = (message) => {
|
|
551
|
-
let parsed;
|
|
552
|
-
try {
|
|
553
|
-
parsed = JSON.parse(message);
|
|
554
|
-
} catch {
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
if (typeof parsed !== "object" || parsed === null)
|
|
558
|
-
return;
|
|
559
|
-
const { o, t, d, b } = parsed;
|
|
560
|
-
if (typeof o !== "string" || typeof t !== "string" || typeof d !== "string") {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
return { origin: o, topic: t, data: b ? Buffer.from(d, "base64") : d };
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
class WsRelay {
|
|
567
|
-
constructor() {
|
|
568
|
-
if (new.target === WsRelay) {
|
|
569
|
-
throw new AppError4("WsRelay is a contract, not an implementation. Bind one with " + "WsRelayModule.forRoot() for Redis or WsRelayModule.forPostgres() " + "for Postgres, or extend it with a relay of your own.");
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// src/server/context.ts
|
|
575
|
-
var EMPTY = new Map;
|
|
576
|
-
var buildContext = (route) => {
|
|
577
|
-
const record = route.meta ?? EMPTY;
|
|
578
|
-
return Object.freeze({
|
|
579
|
-
controller: route.controller,
|
|
580
|
-
handler: route.handlerName,
|
|
581
|
-
method: route.method,
|
|
582
|
-
path: route.path,
|
|
583
|
-
parsesBody: route.options?.body !== undefined,
|
|
584
|
-
get: (key) => record.get(key.id)
|
|
585
|
-
});
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
// src/server/cors.ts
|
|
589
|
-
var ORIGIN = "access-control-allow-origin";
|
|
590
|
-
var allowedOrigin = (options, requested) => {
|
|
591
|
-
const origin = options.origin ?? "*";
|
|
592
|
-
if (typeof origin === "string") {
|
|
593
|
-
if (origin !== "*")
|
|
594
|
-
return origin === requested ? origin : undefined;
|
|
595
|
-
if (!options.credentials)
|
|
596
|
-
return "*";
|
|
597
|
-
return requested ?? undefined;
|
|
598
|
-
}
|
|
599
|
-
if (requested === null)
|
|
600
|
-
return;
|
|
601
|
-
const allowed = typeof origin === "function" ? origin(requested) : origin.includes(requested);
|
|
602
|
-
return allowed ? requested : undefined;
|
|
603
|
-
};
|
|
604
|
-
var applyCors = (options, req, response) => {
|
|
605
|
-
const origin = allowedOrigin(options, req.headers.get("origin"));
|
|
606
|
-
if (origin === undefined)
|
|
607
|
-
return response;
|
|
608
|
-
response.headers.set(ORIGIN, origin);
|
|
609
|
-
if (origin !== "*")
|
|
610
|
-
response.headers.append("vary", "Origin");
|
|
611
|
-
if (options.credentials) {
|
|
612
|
-
response.headers.set("access-control-allow-credentials", "true");
|
|
613
|
-
}
|
|
614
|
-
if (options.exposedHeaders?.length) {
|
|
615
|
-
response.headers.set("access-control-expose-headers", options.exposedHeaders.join(", "));
|
|
616
|
-
}
|
|
617
|
-
return response;
|
|
618
|
-
};
|
|
619
|
-
var withCors = (options, handler) => {
|
|
620
|
-
return async (req) => applyCors(options, req, await handler(req));
|
|
621
|
-
};
|
|
622
|
-
var preflight = (options, methods) => {
|
|
623
|
-
const allowMethods = (options.methods ?? methods).join(", ");
|
|
624
|
-
return async (req) => {
|
|
625
|
-
const response = applyCors(options, req, new Response(null, { status: HttpStatusCode.NO_CONTENT }));
|
|
626
|
-
if (!response.headers.has(ORIGIN))
|
|
627
|
-
return response;
|
|
628
|
-
response.headers.set("access-control-allow-methods", allowMethods);
|
|
629
|
-
const allowHeaders = options.allowedHeaders ?? (req.headers.get("access-control-request-headers") ?? "").split(",").map((header) => header.trim()).filter((header) => header.length > 0);
|
|
630
|
-
if (allowHeaders.length > 0) {
|
|
631
|
-
response.headers.set("access-control-allow-headers", allowHeaders.join(", "));
|
|
632
|
-
}
|
|
633
|
-
if (options.maxAge !== undefined) {
|
|
634
|
-
response.headers.set("access-control-max-age", String(options.maxAge));
|
|
635
|
-
}
|
|
636
|
-
return response;
|
|
637
|
-
};
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
// src/server/middleware.ts
|
|
641
|
-
var compose = (middleware, ctx, handler) => middleware.reduceRight((next, current) => (req) => current.handle(req, ctx, () => next(req)), handler);
|
|
642
|
-
|
|
643
|
-
// src/server/routes.ts
|
|
644
|
-
import { AppError as AppError5 } from "@dunx/core";
|
|
645
|
-
|
|
646
|
-
// src/server/raw-body.ts
|
|
647
|
-
var WANTED = Symbol.for("dunx.http.rawBody.wanted");
|
|
648
|
-
var TEXT = Symbol.for("dunx.http.rawBody.text");
|
|
649
|
-
|
|
650
|
-
class RawBody {
|
|
651
|
-
static want(req) {
|
|
652
|
-
req[WANTED] = true;
|
|
653
|
-
}
|
|
654
|
-
static wanted(req) {
|
|
655
|
-
return req[WANTED] === true;
|
|
656
|
-
}
|
|
657
|
-
static record(req, text) {
|
|
658
|
-
req[TEXT] = text;
|
|
659
|
-
}
|
|
660
|
-
static read(req) {
|
|
661
|
-
return req[TEXT];
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
// src/server/input.ts
|
|
666
|
-
var grouped = (entries) => {
|
|
667
|
-
const collected = {};
|
|
668
|
-
entries.forEach((value, key) => {
|
|
669
|
-
const existing = collected[key];
|
|
670
|
-
if (existing === undefined)
|
|
671
|
-
collected[key] = value;
|
|
672
|
-
else if (Array.isArray(existing))
|
|
673
|
-
existing.push(value);
|
|
674
|
-
else
|
|
675
|
-
collected[key] = [existing, value];
|
|
676
|
-
});
|
|
677
|
-
return collected;
|
|
678
|
-
};
|
|
679
|
-
var asJson = (req) => req.json();
|
|
680
|
-
var asUrlEncoded = async (req) => grouped(new URLSearchParams(await req.text()));
|
|
681
|
-
var asMultipart = async (req) => grouped(await req.formData());
|
|
682
|
-
var asText = (req) => req.text();
|
|
683
|
-
var parserFor = (media) => {
|
|
684
|
-
if (media === "application/json" || media.endsWith("+json"))
|
|
685
|
-
return asJson;
|
|
686
|
-
if (media === "application/x-www-form-urlencoded")
|
|
687
|
-
return asUrlEncoded;
|
|
688
|
-
if (media === "multipart/form-data")
|
|
689
|
-
return asMultipart;
|
|
690
|
-
if (media.startsWith("text/"))
|
|
691
|
-
return asText;
|
|
692
|
-
return;
|
|
693
|
-
};
|
|
694
|
-
var JSON_MEDIA = "application/json";
|
|
695
|
-
var mediaTypeOf = (req) => {
|
|
696
|
-
const header = req.headers.get("content-type");
|
|
697
|
-
if (header === JSON_MEDIA || header === null)
|
|
698
|
-
return JSON_MEDIA;
|
|
699
|
-
const end = header.indexOf(";");
|
|
700
|
-
const media = (end === -1 ? header : header.slice(0, end)).trim();
|
|
701
|
-
return media === "" ? JSON_MEDIA : media.toLowerCase();
|
|
702
|
-
};
|
|
703
|
-
var flatten = (issue) => {
|
|
704
|
-
const path = issue.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
705
|
-
return path === undefined || path === "" ? { message: issue.message } : { message: issue.message, path };
|
|
706
|
-
};
|
|
707
|
-
var accept = (source, result) => {
|
|
708
|
-
if (result.issues !== undefined) {
|
|
709
|
-
throw new ValidationError(source, result.issues.map(flatten));
|
|
710
|
-
}
|
|
711
|
-
return result.value;
|
|
712
|
-
};
|
|
713
|
-
var fillWith = (draft, source, schema, value) => {
|
|
714
|
-
const result = schema["~standard"].validate(value);
|
|
715
|
-
if (result instanceof Promise) {
|
|
716
|
-
return result.then((settled) => {
|
|
717
|
-
draft[source] = accept(source, settled);
|
|
718
|
-
return draft;
|
|
719
|
-
});
|
|
720
|
-
}
|
|
721
|
-
draft[source] = accept(source, result);
|
|
722
|
-
return draft;
|
|
723
|
-
};
|
|
724
|
-
var bodyFill = (schema) => (draft) => {
|
|
725
|
-
const media = mediaTypeOf(draft.req);
|
|
726
|
-
const parse = parserFor(media);
|
|
727
|
-
if (parse === undefined) {
|
|
728
|
-
throw new HttpError(HttpStatusCode.UNSUPPORTED_MEDIA_TYPE, `Unsupported content type "${media}". Declared bodies accept ` + "application/json, application/x-www-form-urlencoded, multipart/form-data or text/*.");
|
|
729
|
-
}
|
|
730
|
-
const read = parse === asJson && RawBody.wanted(draft.req) ? draft.req.text().then((text) => {
|
|
731
|
-
RawBody.record(draft.req, text);
|
|
732
|
-
return JSON.parse(text);
|
|
733
|
-
}) : parse(draft.req);
|
|
734
|
-
return read.then((value) => fillWith(draft, "body", schema, value), (error) => {
|
|
735
|
-
throw new HttpError(HttpStatusCode.BAD_REQUEST, `Malformed ${media} body`, { cause: error });
|
|
736
|
-
});
|
|
737
|
-
};
|
|
738
|
-
var searchOf = (url) => {
|
|
739
|
-
const start = url.indexOf("?");
|
|
740
|
-
if (start === -1)
|
|
741
|
-
return "";
|
|
742
|
-
const end = url.indexOf("#", start + 1);
|
|
743
|
-
return end === -1 ? url.slice(start + 1) : url.slice(start + 1, end);
|
|
744
|
-
};
|
|
745
|
-
var queryFill = (schema) => (draft) => {
|
|
746
|
-
const params = new URLSearchParams(searchOf(draft.req.url));
|
|
747
|
-
return fillWith(draft, "query", schema, grouped(params));
|
|
748
|
-
};
|
|
749
|
-
var paramsFill = (schema) => (draft) => fillWith(draft, "params", schema, draft.req.params);
|
|
750
|
-
var then = (first, second) => (draft) => {
|
|
751
|
-
const started = first(draft);
|
|
752
|
-
return started instanceof Promise ? started.then(second) : second(started);
|
|
753
|
-
};
|
|
754
|
-
var buildInputReader = (options) => {
|
|
755
|
-
const fills = [];
|
|
756
|
-
if (options?.body !== undefined)
|
|
757
|
-
fills.push(bodyFill(options.body));
|
|
758
|
-
if (options?.query !== undefined)
|
|
759
|
-
fills.push(queryFill(options.query));
|
|
760
|
-
if (options?.params !== undefined)
|
|
761
|
-
fills.push(paramsFill(options.params));
|
|
762
|
-
if (fills.length === 0)
|
|
763
|
-
return (req) => ({ req });
|
|
764
|
-
const fill = fills.reduce(then);
|
|
765
|
-
return (req) => fill({ req });
|
|
766
|
-
};
|
|
767
|
-
|
|
768
|
-
// src/server/routes.ts
|
|
769
|
-
var construct = (guard) => new guard;
|
|
770
|
-
var toResponse = (value, status) => {
|
|
771
|
-
if (value instanceof Response)
|
|
772
|
-
return value;
|
|
773
|
-
if (value === undefined || value === null) {
|
|
774
|
-
return new Response(null, { status: HttpStatusCode.NO_CONTENT });
|
|
775
|
-
}
|
|
776
|
-
return Response.json(value, { status });
|
|
777
|
-
};
|
|
778
|
-
var statusFor = (route) => route.options?.status ?? defaultStatusFor(route.method);
|
|
779
|
-
var assertNoCollisions = (discovered) => {
|
|
780
|
-
const owners = new Map;
|
|
781
|
-
for (const route of discovered) {
|
|
782
|
-
const key = `${route.method} ${route.path}`;
|
|
783
|
-
const owner = `${route.controller}.${route.handlerName}`;
|
|
784
|
-
const existing = owners.get(key);
|
|
785
|
-
if (existing !== undefined) {
|
|
786
|
-
throw new AppError5(`Route collision: ${key} is declared by ${existing} and by ${owner}. ` + "Bun would keep only one of them.");
|
|
787
|
-
}
|
|
788
|
-
owners.set(key, owner);
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
var assertNoGatewayCollisions = (discovered, gatewayPaths) => {
|
|
792
|
-
const gateways = new Set(gatewayPaths);
|
|
793
|
-
for (const route of discovered) {
|
|
794
|
-
if (gateways.has(route.path)) {
|
|
795
|
-
throw new AppError5(`Gateway path collision: ${route.path} is served by a gateway and by ` + `${route.controller}.${route.handlerName}(). The upgrade is a route too, ` + "so one of them would be dropped.");
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
var withUpgradeRoutes = (routes, gateways) => {
|
|
800
|
-
const merged = { ...routes };
|
|
801
|
-
for (const [path, upgrade] of gateways)
|
|
802
|
-
merged[path] = { GET: upgrade };
|
|
803
|
-
return merged;
|
|
804
|
-
};
|
|
805
|
-
var unmatchedContext = (req, isPublic) => Object.freeze({
|
|
806
|
-
controller: "(unmatched)",
|
|
807
|
-
handler: "(none)",
|
|
808
|
-
method: req.method,
|
|
809
|
-
path: new URL(req.url).pathname,
|
|
810
|
-
parsesBody: false,
|
|
811
|
-
get: (key) => {
|
|
812
|
-
if (key.id === UNMATCHED.id)
|
|
813
|
-
return true;
|
|
814
|
-
if (key.id === PUBLIC.id && isPublic)
|
|
815
|
-
return true;
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
});
|
|
819
|
-
var buildFallback = (middleware = [], onError = defaultErrorMapper, cors, notFound = "guarded") => {
|
|
820
|
-
const miss = () => {
|
|
821
|
-
throw new HttpError(HttpStatusCode.NOT_FOUND, "NOT_FOUND");
|
|
822
|
-
};
|
|
823
|
-
const run = async (req) => {
|
|
824
|
-
try {
|
|
825
|
-
return await compose(middleware, unmatchedContext(req, notFound === "public"), miss)(req);
|
|
826
|
-
} catch (error) {
|
|
827
|
-
return TraceContext.stamp(onError(error, req), req);
|
|
828
|
-
}
|
|
829
|
-
};
|
|
830
|
-
return cors ? withCors(cors, run) : run;
|
|
831
|
-
};
|
|
832
|
-
var directOr = (guarded, route, read, status, onError, noMiddleware) => {
|
|
833
|
-
if (!noMiddleware)
|
|
834
|
-
return guarded;
|
|
835
|
-
const settle2 = (value, req) => {
|
|
836
|
-
try {
|
|
837
|
-
return toResponse(value, status);
|
|
838
|
-
} catch (error) {
|
|
839
|
-
return onError(error, req);
|
|
840
|
-
}
|
|
841
|
-
};
|
|
842
|
-
const invoke = (input, req) => {
|
|
843
|
-
try {
|
|
844
|
-
const value = route.handler(input);
|
|
845
|
-
return value instanceof Promise ? value.then((resolved) => settle2(resolved, req), (error) => onError(error, req)) : settle2(value, req);
|
|
846
|
-
} catch (error) {
|
|
847
|
-
return onError(error, req);
|
|
848
|
-
}
|
|
849
|
-
};
|
|
850
|
-
return (req) => {
|
|
851
|
-
try {
|
|
852
|
-
const input = read(req);
|
|
853
|
-
return input instanceof Promise ? input.then((resolved) => invoke(resolved, req), (error) => onError(error, req)) : invoke(input, req);
|
|
854
|
-
} catch (error) {
|
|
855
|
-
return onError(error, req);
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
};
|
|
859
|
-
var buildRoutes = (discovered, middleware = [], onError = defaultErrorMapper, cors, resolve = construct) => {
|
|
860
|
-
assertNoCollisions(discovered);
|
|
861
|
-
const routes = {};
|
|
862
|
-
const instances = new Map;
|
|
863
|
-
const guardOf = (guard, from) => {
|
|
864
|
-
const existing = instances.get(guard);
|
|
865
|
-
if (existing)
|
|
866
|
-
return existing;
|
|
867
|
-
const created = resolve(guard, from);
|
|
868
|
-
instances.set(guard, created);
|
|
869
|
-
return created;
|
|
870
|
-
};
|
|
871
|
-
for (const route of discovered) {
|
|
872
|
-
const read = buildInputReader(route.options);
|
|
873
|
-
const status = statusFor(route);
|
|
874
|
-
const chain = [
|
|
875
|
-
...middleware,
|
|
876
|
-
...(route.moduleMiddleware ?? []).map((entry) => guardOf(entry, route.module)),
|
|
877
|
-
...(route.guards ?? []).map((guard) => guardOf(guard, route.module))
|
|
878
|
-
];
|
|
879
|
-
const chained = compose(chain, buildContext(route), async (req) => toResponse(await route.handler(await read(req)), status));
|
|
880
|
-
const guarded = async (req) => {
|
|
881
|
-
try {
|
|
882
|
-
return await chained(req);
|
|
883
|
-
} catch (error) {
|
|
884
|
-
return TraceContext.stamp(onError(error, req), req);
|
|
885
|
-
}
|
|
886
|
-
};
|
|
887
|
-
const byMethod = routes[route.path] ??= {};
|
|
888
|
-
byMethod[route.method] = cors ? withCors(cors, guarded) : directOr(guarded, route, read, status, onError, chain.length === 0);
|
|
889
|
-
}
|
|
890
|
-
if (cors) {
|
|
891
|
-
for (const byMethod of Object.values(routes)) {
|
|
892
|
-
byMethod.OPTIONS = preflight(cors, Object.keys(byMethod));
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
return routes;
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
// src/static/options.ts
|
|
899
|
-
class StaticOptions {
|
|
900
|
-
root;
|
|
901
|
-
path;
|
|
902
|
-
maxAge;
|
|
903
|
-
immutable;
|
|
904
|
-
constructor(init) {
|
|
905
|
-
this.root = init.root;
|
|
906
|
-
this.path = normalizePrefix(init.path ?? "/");
|
|
907
|
-
this.maxAge = init.maxAge ?? 60;
|
|
908
|
-
this.immutable = init.immutable ?? (() => false);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
Object.defineProperty(StaticOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: StaticOptionsInit" }] });
|
|
912
|
-
var normalizePrefix = (path) => {
|
|
913
|
-
const trimmed = path.split("/").filter(Boolean).join("/");
|
|
914
|
-
return trimmed === "" ? "/" : `/${trimmed}`;
|
|
915
|
-
};
|
|
916
|
-
|
|
917
|
-
// src/compression/negotiate.ts
|
|
918
|
-
var quality = (params) => {
|
|
919
|
-
for (const param of params) {
|
|
920
|
-
const [key, value] = param.split("=");
|
|
921
|
-
if (key?.trim().toLowerCase() !== "q")
|
|
922
|
-
continue;
|
|
923
|
-
const q = Number.parseFloat(value ?? "");
|
|
924
|
-
return Number.isFinite(q) && q >= 0 && q <= 1 ? q : 1;
|
|
925
|
-
}
|
|
926
|
-
return 1;
|
|
927
|
-
};
|
|
928
|
-
var negotiate = (header, offered) => {
|
|
929
|
-
if (header === null)
|
|
930
|
-
return;
|
|
931
|
-
const accepted = new Map;
|
|
932
|
-
for (const element of header.split(",")) {
|
|
933
|
-
const [name, ...params] = element.split(";");
|
|
934
|
-
const token = name?.trim().toLowerCase();
|
|
935
|
-
if (token === undefined || token === "")
|
|
936
|
-
continue;
|
|
937
|
-
accepted.set(token, quality(params));
|
|
938
|
-
}
|
|
939
|
-
const wildcard = accepted.get("*");
|
|
940
|
-
let best;
|
|
941
|
-
let bestQuality = 0;
|
|
942
|
-
for (const encoding of offered) {
|
|
943
|
-
const q = accepted.get(encoding) ?? wildcard ?? 0;
|
|
944
|
-
if (q > bestQuality) {
|
|
945
|
-
best = encoding;
|
|
946
|
-
bestQuality = q;
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
return best;
|
|
950
|
-
};
|
|
951
|
-
|
|
952
|
-
// src/compression/options.ts
|
|
953
|
-
var CompressionEncoding = Object.freeze({
|
|
954
|
-
ZSTD: "zstd",
|
|
955
|
-
GZIP: "gzip"
|
|
956
|
-
});
|
|
957
|
-
var COMPRESSIBLE = new Set([
|
|
958
|
-
"application/graphql",
|
|
959
|
-
"application/graphql-response+json",
|
|
960
|
-
"application/javascript",
|
|
961
|
-
"application/json",
|
|
962
|
-
"application/manifest+json",
|
|
963
|
-
"application/wasm",
|
|
964
|
-
"application/x-javascript",
|
|
965
|
-
"application/x-ndjson",
|
|
966
|
-
"application/xml",
|
|
967
|
-
"image/svg+xml"
|
|
968
|
-
]);
|
|
969
|
-
var isCompressibleType = (contentType) => {
|
|
970
|
-
if (contentType === null)
|
|
971
|
-
return false;
|
|
972
|
-
const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
973
|
-
if (type.startsWith("text/"))
|
|
974
|
-
return true;
|
|
975
|
-
if (type.endsWith("+json") || type.endsWith("+xml"))
|
|
976
|
-
return true;
|
|
977
|
-
return COMPRESSIBLE.has(type);
|
|
978
|
-
};
|
|
979
|
-
var encodable = (encoding) => {
|
|
980
|
-
const sync = encoding === CompressionEncoding.ZSTD ? Bun.zstdCompressSync : Bun.gzipSync;
|
|
981
|
-
if (typeof sync !== "function")
|
|
982
|
-
return false;
|
|
983
|
-
try {
|
|
984
|
-
new CompressionStream(encoding);
|
|
985
|
-
return true;
|
|
986
|
-
} catch {
|
|
987
|
-
return false;
|
|
988
|
-
}
|
|
989
|
-
};
|
|
990
|
-
|
|
991
|
-
class CompressionOptions {
|
|
992
|
-
encodings;
|
|
993
|
-
threshold;
|
|
994
|
-
filter;
|
|
995
|
-
constructor(init = {}) {
|
|
996
|
-
this.encodings = init.encodings ?? [
|
|
997
|
-
CompressionEncoding.ZSTD,
|
|
998
|
-
CompressionEncoding.GZIP
|
|
999
|
-
];
|
|
1000
|
-
const missing = this.encodings.filter((encoding) => !encodable(encoding));
|
|
1001
|
-
if (missing.length > 0) {
|
|
1002
|
-
throw new Error(`Bun ${Bun.version} cannot encode ${missing.join(", ")}. ` + "Pass `encodings` without it, or upgrade Bun.");
|
|
1003
|
-
}
|
|
1004
|
-
this.threshold = init.threshold ?? 1024;
|
|
1005
|
-
this.filter = init.filter ?? isCompressibleType;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
Object.defineProperty(CompressionOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: CompressionOptionsInit = {}", optional: true }] });
|
|
1009
|
-
|
|
1010
|
-
// src/ws/redis-relay.ts
|
|
1011
|
-
import { AppError as AppError6 } from "@dunx/core";
|
|
1012
|
-
var PROTOCOLS = [
|
|
1013
|
-
"redis:",
|
|
1014
|
-
"rediss:",
|
|
1015
|
-
"valkey:",
|
|
1016
|
-
"valkeys:",
|
|
1017
|
-
"redis+tls:",
|
|
1018
|
-
"redis+unix:",
|
|
1019
|
-
"redis+tls+unix:"
|
|
1020
|
-
];
|
|
1021
|
-
var defaultRelayUrl = () => process.env["VALKEY_URL"] ?? process.env["REDIS_URL"] ?? "redis://localhost:6379";
|
|
1022
|
-
var assertUrl = (url) => {
|
|
1023
|
-
let parsed;
|
|
1024
|
-
try {
|
|
1025
|
-
parsed = new URL(url);
|
|
1026
|
-
} catch {
|
|
1027
|
-
throw new AppError6(`${JSON.stringify(url)} is not a valid URL for the websocket relay. ` + "Expected something like redis://localhost:6379.");
|
|
1028
|
-
}
|
|
1029
|
-
if (!PROTOCOLS.includes(parsed.protocol)) {
|
|
1030
|
-
throw new AppError6(`Unsupported protocol ${JSON.stringify(parsed.protocol)} in ` + `${JSON.stringify(url)}. Expected one of ${PROTOCOLS.join(", ")}.`);
|
|
1031
|
-
}
|
|
1032
|
-
return url;
|
|
1033
|
-
};
|
|
1034
|
-
|
|
1035
|
-
class RedisRelay extends WsRelay {
|
|
1036
|
-
#url;
|
|
1037
|
-
#options;
|
|
1038
|
-
#pub;
|
|
1039
|
-
#sub;
|
|
1040
|
-
#channel;
|
|
1041
|
-
constructor(options = {}) {
|
|
1042
|
-
super();
|
|
1043
|
-
this.#url = assertUrl(options.url ?? defaultRelayUrl());
|
|
1044
|
-
this.#options = {
|
|
1045
|
-
maxRetries: options.maxRetries ?? 0,
|
|
1046
|
-
...options.connectionTimeout !== undefined && {
|
|
1047
|
-
connectionTimeout: options.connectionTimeout
|
|
1048
|
-
},
|
|
1049
|
-
...options.tls !== undefined && { tls: options.tls }
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
get url() {
|
|
1053
|
-
const parsed = new URL(this.#url);
|
|
1054
|
-
if (parsed.password)
|
|
1055
|
-
parsed.password = "***";
|
|
1056
|
-
return parsed.toString();
|
|
1057
|
-
}
|
|
1058
|
-
async publish(channel, message) {
|
|
1059
|
-
const client = this.#pub ??= new Bun.RedisClient(this.#url, this.#options);
|
|
1060
|
-
try {
|
|
1061
|
-
return await client.publish(channel, message);
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
if (this.#pub === client) {
|
|
1064
|
-
this.#pub = undefined;
|
|
1065
|
-
client.close();
|
|
1066
|
-
}
|
|
1067
|
-
throw error;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
async subscribe(channel, listener) {
|
|
1071
|
-
const client = this.#sub ??= new Bun.RedisClient(this.#url, this.#options);
|
|
1072
|
-
try {
|
|
1073
|
-
await client.connect();
|
|
1074
|
-
await client.subscribe(channel, listener);
|
|
1075
|
-
this.#channel = channel;
|
|
1076
|
-
} catch (error) {
|
|
1077
|
-
if (this.#sub === client) {
|
|
1078
|
-
this.#sub = undefined;
|
|
1079
|
-
client.close();
|
|
1080
|
-
}
|
|
1081
|
-
throw error;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
async close() {
|
|
1085
|
-
const sub = this.#sub;
|
|
1086
|
-
const channel = this.#channel;
|
|
1087
|
-
this.#pub?.close();
|
|
1088
|
-
this.#pub = undefined;
|
|
1089
|
-
this.#sub = undefined;
|
|
1090
|
-
this.#channel = undefined;
|
|
1091
|
-
if (!sub)
|
|
1092
|
-
return;
|
|
1093
|
-
if (channel !== undefined) {
|
|
1094
|
-
try {
|
|
1095
|
-
await sub.unsubscribe(channel);
|
|
1096
|
-
} catch {}
|
|
1097
|
-
}
|
|
1098
|
-
sub.close();
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
Object.defineProperty(RedisRelay, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "options: RedisRelayOptions = {}", optional: true }] });
|
|
1102
|
-
|
|
1103
|
-
// src/health/report-schema.ts
|
|
1104
|
-
var state = {
|
|
1105
|
-
type: "string",
|
|
1106
|
-
enum: ["up", "down", "unknown"],
|
|
1107
|
-
description: "`unknown` is not `down`: a probe that timed out has told you nothing."
|
|
1108
|
-
};
|
|
1109
|
-
var HEALTH_REPORT_SCHEMA = Object.freeze({
|
|
1110
|
-
$id: "HealthReport",
|
|
1111
|
-
type: "object",
|
|
1112
|
-
description: "What the probe found. `up` answers 200 and anything else answers 503.",
|
|
1113
|
-
properties: {
|
|
1114
|
-
status: state,
|
|
1115
|
-
draining: {
|
|
1116
|
-
type: "boolean",
|
|
1117
|
-
description: "The process is shutting down, or something holds it out."
|
|
1118
|
-
},
|
|
1119
|
-
uptimeMs: {
|
|
1120
|
-
type: "integer",
|
|
1121
|
-
description: "Measured on a monotonic clock, so it never goes backwards."
|
|
1122
|
-
},
|
|
1123
|
-
checks: {
|
|
1124
|
-
type: "array",
|
|
1125
|
-
items: {
|
|
1126
|
-
type: "object",
|
|
1127
|
-
properties: {
|
|
1128
|
-
name: { type: "string" },
|
|
1129
|
-
state,
|
|
1130
|
-
critical: {
|
|
1131
|
-
type: "boolean",
|
|
1132
|
-
description: "A failure here sheds traffic. Memory and disk do not."
|
|
1133
|
-
},
|
|
1134
|
-
ms: { type: "integer", description: "How long the check took." },
|
|
1135
|
-
detail: {
|
|
1136
|
-
type: "string",
|
|
1137
|
-
description: "A latency, a version, or a failure message."
|
|
1138
|
-
}
|
|
1139
|
-
},
|
|
1140
|
-
required: ["name", "state", "critical", "ms"]
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
},
|
|
1144
|
-
required: ["status", "draining", "uptimeMs", "checks"]
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
// src/health/registry.ts
|
|
1148
|
-
var bounded = async (indicator, timeoutMs) => {
|
|
1149
|
-
let timer;
|
|
1150
|
-
const timeout = new Promise((resolve) => {
|
|
1151
|
-
timer = setTimeout(() => resolve({ state: "unknown", detail: `no answer in ${timeoutMs} ms` }), timeoutMs);
|
|
1152
|
-
timer.unref?.();
|
|
1153
|
-
});
|
|
1154
|
-
try {
|
|
1155
|
-
return await Promise.race([
|
|
1156
|
-
Promise.resolve().then(() => indicator.check()).catch((error) => ({
|
|
1157
|
-
state: "down",
|
|
1158
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
1159
|
-
})),
|
|
1160
|
-
timeout
|
|
1161
|
-
]);
|
|
1162
|
-
} finally {
|
|
1163
|
-
if (timer)
|
|
1164
|
-
clearTimeout(timer);
|
|
1165
|
-
}
|
|
1166
|
-
};
|
|
1167
|
-
var worst = (checks) => {
|
|
1168
|
-
const critical = checks.filter((check) => check.critical);
|
|
1169
|
-
if (critical.some((check) => check.state === "down"))
|
|
1170
|
-
return "down";
|
|
1171
|
-
if (critical.some((check) => check.state === "unknown"))
|
|
1172
|
-
return "unknown";
|
|
1173
|
-
return "up";
|
|
1174
|
-
};
|
|
1175
|
-
|
|
1176
|
-
class HealthOptions {
|
|
1177
|
-
liveness;
|
|
1178
|
-
readiness;
|
|
1179
|
-
timeoutMs;
|
|
1180
|
-
routes;
|
|
1181
|
-
documented;
|
|
1182
|
-
drainDelayMs;
|
|
1183
|
-
constructor(init = {}) {
|
|
1184
|
-
this.liveness = init.liveness ?? [];
|
|
1185
|
-
this.readiness = init.readiness ?? [];
|
|
1186
|
-
this.timeoutMs = init.timeoutMs ?? 2000;
|
|
1187
|
-
this.routes = init.routes ?? true;
|
|
1188
|
-
this.documented = init.documented ?? true;
|
|
1189
|
-
this.drainDelayMs = Math.max(0, init.drainDelayMs ?? 0);
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
Object.defineProperty(HealthOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: HealthOptionsInit = {}", optional: true }] });
|
|
1193
|
-
|
|
1194
|
-
class HealthRegistry {
|
|
1195
|
-
options;
|
|
1196
|
-
readiness_;
|
|
1197
|
-
#startedAt = performance.now();
|
|
1198
|
-
constructor(options, readiness_) {
|
|
1199
|
-
this.options = options;
|
|
1200
|
-
this.readiness_ = readiness_;
|
|
1201
|
-
}
|
|
1202
|
-
async report(indicators) {
|
|
1203
|
-
const checks = await Promise.all(indicators.map(async (indicator) => {
|
|
1204
|
-
const started = performance.now();
|
|
1205
|
-
const result = await bounded(indicator, this.options.timeoutMs);
|
|
1206
|
-
return {
|
|
1207
|
-
name: indicator.name,
|
|
1208
|
-
state: result.state,
|
|
1209
|
-
critical: indicator.critical,
|
|
1210
|
-
ms: Math.round(performance.now() - started),
|
|
1211
|
-
...result.detail === undefined ? {} : { detail: result.detail }
|
|
1212
|
-
};
|
|
1213
|
-
}));
|
|
1214
|
-
return {
|
|
1215
|
-
status: worst(checks),
|
|
1216
|
-
draining: this.readiness_.draining,
|
|
1217
|
-
uptimeMs: Math.round(performance.now() - this.#startedAt),
|
|
1218
|
-
checks
|
|
1219
|
-
};
|
|
1220
|
-
}
|
|
1221
|
-
liveness() {
|
|
1222
|
-
return this.report(this.options.liveness);
|
|
1223
|
-
}
|
|
1224
|
-
async readiness() {
|
|
1225
|
-
const report = await this.report(this.options.readiness);
|
|
1226
|
-
if (!this.readiness_.draining)
|
|
1227
|
-
return report;
|
|
1228
|
-
return {
|
|
1229
|
-
...report,
|
|
1230
|
-
status: "down",
|
|
1231
|
-
checks: [
|
|
1232
|
-
{
|
|
1233
|
-
name: "readiness",
|
|
1234
|
-
state: "down",
|
|
1235
|
-
critical: true,
|
|
1236
|
-
ms: 0,
|
|
1237
|
-
detail: this.readiness_.reason ?? "not accepting traffic"
|
|
1238
|
-
},
|
|
1239
|
-
...report.checks
|
|
1240
|
-
]
|
|
1241
|
-
};
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
Object.defineProperty(HealthRegistry, Symbol.for("dunx.deps"), { value: () => [HealthOptions, { unresolved: "private readonly readiness_: Readiness", typeOnly: "Readiness" }] });
|
|
1245
|
-
|
|
1246
|
-
// src/health/controller.ts
|
|
1247
|
-
import { inject } from "@dunx/core";
|
|
1248
|
-
var probeResponses = {
|
|
1249
|
-
response: { 200: HEALTH_REPORT_SCHEMA, 503: HEALTH_REPORT_SCHEMA }
|
|
1250
|
-
};
|
|
1251
|
-
var answer = (report) => Response.json(report, { status: report.status === "up" ? 200 : 503 });
|
|
1252
|
-
var _dec = [
|
|
1253
|
-
Controller("health")
|
|
1254
|
-
];
|
|
1255
|
-
var _dec2 = [
|
|
1256
|
-
Public(),
|
|
1257
|
-
Get("/live", probeResponses)
|
|
1258
|
-
];
|
|
1259
|
-
var _dec3 = [
|
|
1260
|
-
Public(),
|
|
1261
|
-
Get("/ready", probeResponses)
|
|
1262
|
-
];
|
|
1263
|
-
var _health = new WeakMap;
|
|
1264
|
-
var _init = __decoratorStart(undefined);
|
|
1265
|
-
|
|
1266
|
-
class HealthController {
|
|
1267
|
-
constructor() {
|
|
1268
|
-
__privateAdd(this, _health, inject(HealthRegistry));
|
|
1269
|
-
__runInitializers(_init, 5, this);
|
|
1270
|
-
}
|
|
1271
|
-
async live() {
|
|
1272
|
-
return answer(await __privateGet(this, _health).liveness());
|
|
1273
|
-
}
|
|
1274
|
-
async ready() {
|
|
1275
|
-
return answer(await __privateGet(this, _health).readiness());
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
__decorateElement(_init, 1, "live", _dec2, HealthController);
|
|
1279
|
-
__decorateElement(_init, 1, "ready", _dec3, HealthController);
|
|
1280
|
-
HealthController = __decorateElement(_init, 0, "HealthController", _dec, HealthController);
|
|
1281
|
-
__runInitializers(_init, 1, HealthController);
|
|
1282
|
-
__decoratorMetadata(_init, HealthController);
|
|
1283
|
-
let _HealthController = HealthController;
|
|
1284
|
-
var _dec = [
|
|
1285
|
-
ApiHidden()
|
|
1286
|
-
];
|
|
1287
|
-
var _base = HealthController;
|
|
1288
|
-
var _init = __decoratorStart(_base);
|
|
1289
|
-
|
|
1290
|
-
class HiddenHealthController extends _base {
|
|
1291
|
-
}
|
|
1292
|
-
HiddenHealthController = __decorateElement(_init, 0, "HiddenHealthController", _dec, HiddenHealthController);
|
|
1293
|
-
__runInitializers(_init, 1, HiddenHealthController);
|
|
1294
|
-
__decoratorMetadata(_init, HiddenHealthController);
|
|
1295
|
-
let _HiddenHealthController = HiddenHealthController;
|
|
1296
|
-
|
|
1297
|
-
export { defaultStatusFor, Controller, Get, Post, Put, Patch, Delete, metaKey, meta, ROLES, PUBLIC, HIDDEN, UNMATCHED, Roles, Public, ApiHidden, UseGuards, guardsOf, metaOf, mergeMeta, HttpError, ValidationError, ErrorFilter, isErrorFilter, toErrorMapper, errorMapper, defaultErrorMapper, joinPath, discoverRoutes, encode, decode, HandlerKind, markHandler, markGateway, isGateway, composeSocket, observe, buildRuntime, buildGateways, buildWebSocket, normalizePath, discoverGateway, discoverGateways, DEFAULT_RELAY_CHANNEL, defaultRelayError, encodeRelay, decodeRelay, WsRelay, RawBody, buildContext, withCors, preflight, compose, assertNoCollisions, assertNoGatewayCollisions, withUpgradeRoutes, buildFallback, buildRoutes, StaticOptions, normalizePrefix, negotiate, CompressionEncoding, isCompressibleType, CompressionOptions, defaultRelayUrl, RedisRelay, HEALTH_REPORT_SCHEMA, HealthOptions, HealthRegistry, HealthController, HiddenHealthController };
|