@dunx/http 3.5.1 → 3.7.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 +14 -4
- package/dist/chunk-08k9vq31.js +94 -0
- package/dist/{chunk-p9hdmkm6.js → chunk-1jt27yka.js} +8 -83
- package/dist/chunk-3eecdh6d.js +160 -0
- package/dist/client/json.d.ts +12 -11
- package/dist/client/options.d.ts +3 -3
- package/dist/client/retry.d.ts +17 -31
- package/dist/client/service.d.ts +21 -12
- package/dist/client/sse.d.ts +39 -0
- package/dist/client.d.ts +10 -1
- package/dist/client.js +166 -108
- package/dist/connect/middleware.d.ts +25 -0
- package/dist/connect/module.d.ts +29 -0
- package/dist/connect/options.d.ts +64 -0
- package/dist/connect/registry.d.ts +47 -0
- package/dist/connect.d.ts +12 -0
- package/dist/connect.js +208 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +253 -184
- package/dist/internal.d.ts +8 -10
- package/dist/internal.js +7 -3
- package/dist/route/claims.d.ts +11 -0
- package/dist/route/metadata.d.ts +11 -0
- package/dist/route/prefix.d.ts +6 -0
- package/dist/server/application.d.ts +1 -1
- package/dist/server/binding.d.ts +4 -2
- package/dist/server/claimed-routes.d.ts +15 -0
- package/dist/server/cors.d.ts +2 -2
- package/dist/server/middleware.d.ts +14 -2
- package/dist/server/options-provider.d.ts +3 -0
- package/dist/server/options.d.ts +9 -1
- package/dist/server/routes.d.ts +7 -2
- package/dist/sse/decorators.d.ts +28 -0
- package/dist/sse/event.d.ts +19 -0
- package/dist/sse/stream.d.ts +35 -0
- package/dist/static/files.d.ts +7 -0
- package/dist/static/options.d.ts +2 -2
- package/dist/throttle/guard.d.ts +3 -1
- package/package.json +19 -2
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
PUBLIC2,
|
|
13
13
|
HIDDEN2,
|
|
14
14
|
UNMATCHED2,
|
|
15
|
+
STREAMS2,
|
|
16
|
+
REQUEST_SERVER,
|
|
15
17
|
Roles2,
|
|
16
18
|
Public2,
|
|
17
19
|
ApiHidden2,
|
|
@@ -19,89 +21,32 @@ import {
|
|
|
19
21
|
metaOf2,
|
|
20
22
|
mergeMeta2,
|
|
21
23
|
discoverRoutes2,
|
|
22
|
-
RoutePrefix2
|
|
24
|
+
RoutePrefix2
|
|
25
|
+
} from "./chunk-1jt27yka.js";
|
|
26
|
+
import {
|
|
23
27
|
HandlerKind,
|
|
24
28
|
markHandler,
|
|
25
29
|
markGateway,
|
|
26
30
|
discoverGateways,
|
|
27
|
-
buildContext2
|
|
28
|
-
|
|
31
|
+
buildContext2,
|
|
32
|
+
StaticOptions2,
|
|
33
|
+
StaticFiles2
|
|
34
|
+
} from "./chunk-3eecdh6d.js";
|
|
29
35
|
import {
|
|
30
36
|
TRACEPARENT_HEADER2,
|
|
31
37
|
TRACESTATE_HEADER2,
|
|
32
38
|
TRACERESPONSE_HEADER2,
|
|
33
39
|
TraceContext2
|
|
34
40
|
} from "./chunk-gmtwad7f.js";
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return target;
|
|
45
|
-
};
|
|
46
|
-
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
|
|
47
|
-
var __typeError = (msg) => {
|
|
48
|
-
throw TypeError(msg);
|
|
49
|
-
};
|
|
50
|
-
var __defNormalProp = (obj, key, value) => (key in obj) ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
51
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
52
|
-
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
|
|
53
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
54
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
55
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
56
|
-
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
57
|
-
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
|
|
58
|
-
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
|
|
59
|
-
var __expectFn = (fn) => fn !== undefined && typeof fn !== "function" ? __typeError("Function expected") : fn;
|
|
60
|
-
var __decoratorContext = (kind, name, done, metadata, fns) => ({
|
|
61
|
-
kind: __decoratorStrings[kind],
|
|
62
|
-
name,
|
|
63
|
-
metadata,
|
|
64
|
-
addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null))
|
|
65
|
-
});
|
|
66
|
-
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
|
|
67
|
-
var __runInitializers = (array, flags, self, value) => {
|
|
68
|
-
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length;i < n; i++)
|
|
69
|
-
flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
|
|
70
|
-
return value;
|
|
71
|
-
};
|
|
72
|
-
var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
73
|
-
var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
|
|
74
|
-
var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
|
|
75
|
-
var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
|
|
76
|
-
var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : {
|
|
77
|
-
get [name]() {
|
|
78
|
-
return __privateGet(this, extra);
|
|
79
|
-
},
|
|
80
|
-
set [name](x) {
|
|
81
|
-
__privateSet(this, extra, x);
|
|
82
|
-
}
|
|
83
|
-
}, name));
|
|
84
|
-
k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
|
|
85
|
-
for (var i = decorators.length - 1;i >= 0; i--) {
|
|
86
|
-
ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
|
|
87
|
-
if (k) {
|
|
88
|
-
ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => (name in x) };
|
|
89
|
-
if (k ^ 3)
|
|
90
|
-
access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
|
|
91
|
-
if (k > 2)
|
|
92
|
-
access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
|
|
93
|
-
}
|
|
94
|
-
it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? undefined : { get: desc.get, set: desc.set } : target, ctx);
|
|
95
|
-
done._ = 1;
|
|
96
|
-
if (k ^ 4 || it === undefined)
|
|
97
|
-
__expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
|
|
98
|
-
else if (typeof it !== "object" || it === null)
|
|
99
|
-
__typeError("Object expected");
|
|
100
|
-
else
|
|
101
|
-
__expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
|
|
102
|
-
}
|
|
103
|
-
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
104
|
-
};
|
|
41
|
+
import {
|
|
42
|
+
__privateGet,
|
|
43
|
+
__privateAdd,
|
|
44
|
+
__decoratorStart,
|
|
45
|
+
__decoratorMetadata,
|
|
46
|
+
__runInitializers,
|
|
47
|
+
__decorateElement,
|
|
48
|
+
PathClaims
|
|
49
|
+
} from "./chunk-08k9vq31.js";
|
|
105
50
|
|
|
106
51
|
// src/route/decorators.ts
|
|
107
52
|
var Controller = (prefix = "") => (target) => {
|
|
@@ -217,6 +162,17 @@ import {
|
|
|
217
162
|
RequestContext as RequestContext3
|
|
218
163
|
} from "@dunx/core";
|
|
219
164
|
|
|
165
|
+
// src/server/claimed-routes.ts
|
|
166
|
+
class ClaimedRoutes {
|
|
167
|
+
#paths = new Set;
|
|
168
|
+
attach(paths) {
|
|
169
|
+
this.#paths = new Set(paths);
|
|
170
|
+
}
|
|
171
|
+
has(path) {
|
|
172
|
+
return this.#paths.has(path);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
220
176
|
// src/server/metrics.ts
|
|
221
177
|
import { Durations } from "@dunx/core";
|
|
222
178
|
var UNMATCHED_ROUTE = "(unmatched)";
|
|
@@ -922,6 +878,10 @@ import {
|
|
|
922
878
|
teardownFailures as toFailures
|
|
923
879
|
} from "@dunx/core";
|
|
924
880
|
|
|
881
|
+
// src/server/middleware.ts
|
|
882
|
+
var compose = (middleware, ctx, handler) => middleware.reduceRight((next, current) => (req) => current.handle(req, ctx, () => next(req)), handler);
|
|
883
|
+
var hasClaimedPaths = (value) => typeof value.claimedPaths === "function";
|
|
884
|
+
|
|
925
885
|
// src/server/request-logging.ts
|
|
926
886
|
import {
|
|
927
887
|
Logger as Logger2,
|
|
@@ -1213,7 +1173,7 @@ var applyCors = (options, req, response) => {
|
|
|
1213
1173
|
return response;
|
|
1214
1174
|
};
|
|
1215
1175
|
var withCors = (options, handler) => {
|
|
1216
|
-
return async (req) => applyCors(options, req, await handler(req));
|
|
1176
|
+
return async (req, server) => applyCors(options, req, await handler(req, server));
|
|
1217
1177
|
};
|
|
1218
1178
|
var preflight = (options, methods) => {
|
|
1219
1179
|
const allowMethods = (options.methods ?? methods).join(", ");
|
|
@@ -1336,9 +1296,6 @@ var buildInputReader = (options) => {
|
|
|
1336
1296
|
return (req) => fill({ req });
|
|
1337
1297
|
};
|
|
1338
1298
|
|
|
1339
|
-
// src/server/middleware.ts
|
|
1340
|
-
var compose = (middleware, ctx, handler) => middleware.reduceRight((next, current) => (req) => current.handle(req, ctx, () => next(req)), handler);
|
|
1341
|
-
|
|
1342
1299
|
// src/server/routes.ts
|
|
1343
1300
|
var construct = (guard) => new guard;
|
|
1344
1301
|
var toResponse = (value, status) => {
|
|
@@ -1351,15 +1308,9 @@ var toResponse = (value, status) => {
|
|
|
1351
1308
|
};
|
|
1352
1309
|
var statusFor = (route) => route.options?.status ?? defaultStatusFor2(route.method);
|
|
1353
1310
|
var assertNoCollisions = (discovered) => {
|
|
1354
|
-
const
|
|
1311
|
+
const claims = new PathClaims("Route", "Bun would keep only one of them.");
|
|
1355
1312
|
for (const route of discovered) {
|
|
1356
|
-
|
|
1357
|
-
const owner = `${route.controller}.${route.handlerName}`;
|
|
1358
|
-
const existing = owners.get(key);
|
|
1359
|
-
if (existing !== undefined) {
|
|
1360
|
-
throw new AppError6(`Route collision: ${key} is declared by ${existing} and by ${owner}. ` + "Bun would keep only one of them.");
|
|
1361
|
-
}
|
|
1362
|
-
owners.set(key, owner);
|
|
1313
|
+
claims.claim(`${route.method} ${route.path}`, `${route.controller}.${route.handlerName}`);
|
|
1363
1314
|
}
|
|
1364
1315
|
};
|
|
1365
1316
|
var assertNoGatewayCollisions = (discovered, gatewayPaths) => {
|
|
@@ -1376,7 +1327,7 @@ var withUpgradeRoutes = (routes, gateways) => {
|
|
|
1376
1327
|
merged[path] = { GET: upgrade };
|
|
1377
1328
|
return merged;
|
|
1378
1329
|
};
|
|
1379
|
-
var unmatchedContext = (req, isPublic) => Object.freeze({
|
|
1330
|
+
var unmatchedContext = (req, isPublic, server) => Object.freeze({
|
|
1380
1331
|
controller: "(unmatched)",
|
|
1381
1332
|
handler: "(none)",
|
|
1382
1333
|
method: req.method,
|
|
@@ -1387,16 +1338,37 @@ var unmatchedContext = (req, isPublic) => Object.freeze({
|
|
|
1387
1338
|
return true;
|
|
1388
1339
|
if (key.id === PUBLIC2.id && isPublic)
|
|
1389
1340
|
return true;
|
|
1341
|
+
if (key.id === REQUEST_SERVER.id)
|
|
1342
|
+
return server;
|
|
1390
1343
|
return;
|
|
1391
1344
|
}
|
|
1392
1345
|
});
|
|
1346
|
+
var assertNoShadowedClaims = (discovered, middleware) => {
|
|
1347
|
+
const routed = new Map;
|
|
1348
|
+
for (const route of discovered) {
|
|
1349
|
+
if (!routed.has(route.path)) {
|
|
1350
|
+
routed.set(route.path, `${route.controller}.${route.handlerName}`);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
for (const entry of middleware) {
|
|
1354
|
+
if (!hasClaimedPaths(entry))
|
|
1355
|
+
continue;
|
|
1356
|
+
const owner = entry.constructor.name;
|
|
1357
|
+
for (const path of entry.claimedPaths()) {
|
|
1358
|
+
const taken = routed.get(path);
|
|
1359
|
+
if (taken !== undefined) {
|
|
1360
|
+
throw new AppError6(`Path collision: ${path} is declared by ${taken} and claimed by ` + `${owner}. A route wins, so the middleware would never see it.`);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1393
1365
|
var buildFallback = (middleware = [], onError = defaultErrorMapper, cors, notFound = "guarded") => {
|
|
1394
1366
|
const miss = () => {
|
|
1395
1367
|
throw new HttpError(HttpStatusCode2.NOT_FOUND, "NOT_FOUND");
|
|
1396
1368
|
};
|
|
1397
|
-
const run = async (req) => {
|
|
1369
|
+
const run = async (req, server) => {
|
|
1398
1370
|
try {
|
|
1399
|
-
return await compose(middleware, unmatchedContext(req, notFound === "public"), miss)(req);
|
|
1371
|
+
return await compose(middleware, unmatchedContext(req, notFound === "public", server), miss)(req);
|
|
1400
1372
|
} catch (error) {
|
|
1401
1373
|
return TraceContext2.stamp(onError(error, req), req);
|
|
1402
1374
|
}
|
|
@@ -1450,7 +1422,8 @@ var buildRoutes = (discovered, middleware = [], onError = defaultErrorMapper, co
|
|
|
1450
1422
|
...(route.moduleMiddleware ?? []).map((entry) => guardOf(entry, route.module)),
|
|
1451
1423
|
...(route.guards ?? []).map((guard) => guardOf(guard, route.module))
|
|
1452
1424
|
];
|
|
1453
|
-
const
|
|
1425
|
+
const context = buildContext2(route);
|
|
1426
|
+
const chained = compose(chain, context, async (req) => toResponse(await route.handler(await read(req)), status));
|
|
1454
1427
|
const guarded = async (req) => {
|
|
1455
1428
|
try {
|
|
1456
1429
|
return await chained(req);
|
|
@@ -1458,8 +1431,12 @@ var buildRoutes = (discovered, middleware = [], onError = defaultErrorMapper, co
|
|
|
1458
1431
|
return TraceContext2.stamp(onError(error, req), req);
|
|
1459
1432
|
}
|
|
1460
1433
|
};
|
|
1434
|
+
const served = cors ? withCors(cors, guarded) : directOr(guarded, route, read, status, onError, chain.length === 0);
|
|
1461
1435
|
const byMethod = routes[route.path] ??= {};
|
|
1462
|
-
byMethod[route.method] =
|
|
1436
|
+
byMethod[route.method] = context.get(STREAMS2) === true ? (req, server) => {
|
|
1437
|
+
server?.timeout(req, 0);
|
|
1438
|
+
return served(req, server);
|
|
1439
|
+
} : served;
|
|
1463
1440
|
}
|
|
1464
1441
|
if (cors) {
|
|
1465
1442
|
for (const byMethod of Object.values(routes)) {
|
|
@@ -1488,7 +1465,7 @@ class ServerBinding {
|
|
|
1488
1465
|
}
|
|
1489
1466
|
bind(plan) {
|
|
1490
1467
|
const { websocket: ws } = plan;
|
|
1491
|
-
const { http2, http1, gatewayPort } = this.#protocols;
|
|
1468
|
+
const { http2, http1, gatewayPort, idleTimeout } = this.#protocols;
|
|
1492
1469
|
const split = ws !== undefined && gatewayPort !== undefined;
|
|
1493
1470
|
const table = ws && !split ? {
|
|
1494
1471
|
routes: withUpgradeRoutes(plan.routes, ws.routes),
|
|
@@ -1499,6 +1476,7 @@ class ServerBinding {
|
|
|
1499
1476
|
fetch: plan.fetch,
|
|
1500
1477
|
...http2 !== undefined && { http2 },
|
|
1501
1478
|
...http1 !== undefined && { http1 },
|
|
1479
|
+
...idleTimeout !== undefined && { idleTimeout },
|
|
1502
1480
|
...table
|
|
1503
1481
|
});
|
|
1504
1482
|
if (split) {
|
|
@@ -1506,6 +1484,7 @@ class ServerBinding {
|
|
|
1506
1484
|
this.#gateways = Bun.serve({
|
|
1507
1485
|
port: gatewayPort,
|
|
1508
1486
|
fetch: plan.fetch,
|
|
1487
|
+
...idleTimeout !== undefined && { idleTimeout },
|
|
1509
1488
|
routes: withUpgradeRoutes({}, ws.routes),
|
|
1510
1489
|
websocket: ws.websocket
|
|
1511
1490
|
});
|
|
@@ -1598,7 +1577,8 @@ class HttpApplication extends ShutdownAware {
|
|
|
1598
1577
|
this.#binding = new ServerBinding({
|
|
1599
1578
|
http2: options.http2,
|
|
1600
1579
|
http1: options.http1,
|
|
1601
|
-
gatewayPort: options.gatewayPort
|
|
1580
|
+
gatewayPort: options.gatewayPort,
|
|
1581
|
+
idleTimeout: options.idleTimeout
|
|
1602
1582
|
});
|
|
1603
1583
|
this.#split = options.gatewayPort !== undefined && websocket !== undefined;
|
|
1604
1584
|
this.gatewayPaths = websocket?.paths ?? [];
|
|
@@ -1664,6 +1644,8 @@ class HttpApplication extends ShutdownAware {
|
|
|
1664
1644
|
const ws = this.#websocket;
|
|
1665
1645
|
if (ws && !this.#split)
|
|
1666
1646
|
assertNoGatewayCollisions(prefixed, ws.paths);
|
|
1647
|
+
assertNoShadowedClaims(prefixed, middleware);
|
|
1648
|
+
this.#app.get(ClaimedRoutes).attach(middleware.flatMap((entry) => hasClaimedPaths(entry) ? [...entry.claimedPaths()] : []));
|
|
1667
1649
|
const fetch = buildFallback(middleware, this.#onError, this.#cors, this.#notFound);
|
|
1668
1650
|
const bound = this.#binding.bind({ port, routes, fetch, websocket: ws });
|
|
1669
1651
|
attachAddressSource(this.#app.get(ClientAddress), {
|
|
@@ -1805,6 +1787,9 @@ class HttpOptionsProvider {
|
|
|
1805
1787
|
get gatewayPort() {
|
|
1806
1788
|
return;
|
|
1807
1789
|
}
|
|
1790
|
+
get idleTimeout() {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1808
1793
|
}
|
|
1809
1794
|
|
|
1810
1795
|
class DefaultHttpOptions extends HttpOptionsProvider {
|
|
@@ -1831,7 +1816,8 @@ function resolveHttpOptions(settings, given) {
|
|
|
1831
1816
|
relayResubscribe: settings.relayResubscribe,
|
|
1832
1817
|
http2: settings.http2,
|
|
1833
1818
|
http1: settings.http1,
|
|
1834
|
-
gatewayPort: settings.gatewayPort
|
|
1819
|
+
gatewayPort: settings.gatewayPort,
|
|
1820
|
+
idleTimeout: settings.idleTimeout
|
|
1835
1821
|
};
|
|
1836
1822
|
for (const key of Object.keys(given)) {
|
|
1837
1823
|
merged[key] = given[key];
|
|
@@ -1866,7 +1852,13 @@ class HttpFactory {
|
|
|
1866
1852
|
useFactory: (logger, context, settings) => new SocketLoggingMiddleware(logger, context, pick(options.socketLogging, settings.socketLogging)),
|
|
1867
1853
|
inject: [Logger4, RequestContext3, HttpOptionsProvider]
|
|
1868
1854
|
});
|
|
1869
|
-
const services = [
|
|
1855
|
+
const services = [
|
|
1856
|
+
PubSub,
|
|
1857
|
+
ClientAddress,
|
|
1858
|
+
RequestMetrics,
|
|
1859
|
+
RoutePrefix2,
|
|
1860
|
+
ClaimedRoutes
|
|
1861
|
+
];
|
|
1870
1862
|
const providers = [...services, logging, metricsMiddleware, socketLogging];
|
|
1871
1863
|
const scope = {
|
|
1872
1864
|
module: HttpModule,
|
|
@@ -1921,89 +1913,14 @@ class HttpFactory {
|
|
|
1921
1913
|
return entries.map((entry) => app.get(entry, root));
|
|
1922
1914
|
}
|
|
1923
1915
|
}
|
|
1924
|
-
// src/static/files.ts
|
|
1925
|
-
import { join, normalize, resolve } from "path";
|
|
1926
|
-
|
|
1927
|
-
// src/static/options.ts
|
|
1928
|
-
class StaticOptions {
|
|
1929
|
-
root;
|
|
1930
|
-
path;
|
|
1931
|
-
maxAge;
|
|
1932
|
-
immutable;
|
|
1933
|
-
constructor(init) {
|
|
1934
|
-
this.root = init.root;
|
|
1935
|
-
this.path = normalizePrefix(init.path ?? "/");
|
|
1936
|
-
this.maxAge = init.maxAge ?? 60;
|
|
1937
|
-
this.immutable = init.immutable ?? (() => false);
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
Object.defineProperty(StaticOptions, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "init: StaticOptionsInit" }] });
|
|
1941
|
-
var normalizePrefix = (path) => {
|
|
1942
|
-
const trimmed = path.split("/").filter(Boolean).join("/");
|
|
1943
|
-
return trimmed === "" ? "/" : `/${trimmed}`;
|
|
1944
|
-
};
|
|
1945
|
-
|
|
1946
|
-
// src/static/files.ts
|
|
1947
|
-
class StaticFiles {
|
|
1948
|
-
#options;
|
|
1949
|
-
#root;
|
|
1950
|
-
#prefix;
|
|
1951
|
-
constructor(options) {
|
|
1952
|
-
this.#options = options;
|
|
1953
|
-
this.#root = resolve(options.root);
|
|
1954
|
-
this.#prefix = options.path === "/" ? "/" : `${options.path}/`;
|
|
1955
|
-
}
|
|
1956
|
-
resolvePath(pathname) {
|
|
1957
|
-
const relative = pathname.startsWith(this.#prefix) ? pathname.slice(this.#prefix.length) : pathname.slice(this.#options.path.length);
|
|
1958
|
-
let decoded;
|
|
1959
|
-
try {
|
|
1960
|
-
decoded = decodeURIComponent(relative);
|
|
1961
|
-
} catch {
|
|
1962
|
-
return;
|
|
1963
|
-
}
|
|
1964
|
-
if (decoded.includes("\x00"))
|
|
1965
|
-
return;
|
|
1966
|
-
const candidate = resolve(join(this.#root, normalize(decoded)));
|
|
1967
|
-
if (candidate !== this.#root && !candidate.startsWith(`${this.#root}/`)) {
|
|
1968
|
-
return;
|
|
1969
|
-
}
|
|
1970
|
-
return candidate;
|
|
1971
|
-
}
|
|
1972
|
-
#cacheControl(pathname) {
|
|
1973
|
-
const { immutable, maxAge } = this.#options;
|
|
1974
|
-
return immutable(pathname) ? "public, max-age=31536000, immutable" : `public, max-age=${maxAge}`;
|
|
1975
|
-
}
|
|
1976
|
-
async handle(req, _ctx, next) {
|
|
1977
|
-
const { pathname } = new URL(req.url);
|
|
1978
|
-
if (pathname !== this.#options.path && !pathname.startsWith(this.#prefix)) {
|
|
1979
|
-
return next();
|
|
1980
|
-
}
|
|
1981
|
-
if (req.method !== "GET" && req.method !== "HEAD")
|
|
1982
|
-
return next();
|
|
1983
|
-
const path = this.resolvePath(pathname);
|
|
1984
|
-
if (path === undefined)
|
|
1985
|
-
return next();
|
|
1986
|
-
const file = Bun.file(path);
|
|
1987
|
-
if (!await file.exists())
|
|
1988
|
-
return next();
|
|
1989
|
-
return new Response(file, {
|
|
1990
|
-
headers: {
|
|
1991
|
-
"cache-control": this.#cacheControl(pathname),
|
|
1992
|
-
...file.type === "" ? { "content-type": "application/octet-stream" } : {},
|
|
1993
|
-
"x-content-type-options": "nosniff"
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
Object.defineProperty(StaticFiles, Symbol.for("dunx.deps"), { value: () => [StaticOptions] });
|
|
1999
1916
|
// src/static/module.ts
|
|
2000
1917
|
import {
|
|
2001
1918
|
Module,
|
|
2002
1919
|
provide as provide2
|
|
2003
1920
|
} from "@dunx/core";
|
|
2004
|
-
var files = () => provide2(
|
|
2005
|
-
useFactory: (options) => new
|
|
2006
|
-
inject: [
|
|
1921
|
+
var files = () => provide2(StaticFiles2, {
|
|
1922
|
+
useFactory: (options) => new StaticFiles2(options),
|
|
1923
|
+
inject: [StaticOptions2]
|
|
2007
1924
|
});
|
|
2008
1925
|
var _dec = [
|
|
2009
1926
|
Module({})
|
|
@@ -2014,9 +1931,9 @@ class StaticModule {
|
|
|
2014
1931
|
static forRoot(init) {
|
|
2015
1932
|
return {
|
|
2016
1933
|
module: StaticModule,
|
|
2017
|
-
exports: [
|
|
1934
|
+
exports: [StaticOptions2, StaticFiles2],
|
|
2018
1935
|
providers: [
|
|
2019
|
-
provide2(
|
|
1936
|
+
provide2(StaticOptions2, { useValue: new StaticOptions2(init) }),
|
|
2020
1937
|
files()
|
|
2021
1938
|
]
|
|
2022
1939
|
};
|
|
@@ -2025,10 +1942,10 @@ class StaticModule {
|
|
|
2025
1942
|
return {
|
|
2026
1943
|
module: StaticModule,
|
|
2027
1944
|
...config.imports && { imports: config.imports },
|
|
2028
|
-
exports: [
|
|
1945
|
+
exports: [StaticOptions2, StaticFiles2],
|
|
2029
1946
|
providers: [
|
|
2030
|
-
provide2(
|
|
2031
|
-
useFactory: async (...deps) => new
|
|
1947
|
+
provide2(StaticOptions2, {
|
|
1948
|
+
useFactory: async (...deps) => new StaticOptions2(await config.useFactory(...deps)),
|
|
2032
1949
|
inject: config.inject ?? []
|
|
2033
1950
|
}),
|
|
2034
1951
|
files()
|
|
@@ -2092,10 +2009,13 @@ var COMPRESSIBLE = new Set([
|
|
|
2092
2009
|
"application/xml",
|
|
2093
2010
|
"image/svg+xml"
|
|
2094
2011
|
]);
|
|
2012
|
+
var ENDLESS = new Set(["text/event-stream"]);
|
|
2095
2013
|
var isCompressibleType = (contentType) => {
|
|
2096
2014
|
if (contentType === null)
|
|
2097
2015
|
return false;
|
|
2098
2016
|
const type = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
2017
|
+
if (ENDLESS.has(type))
|
|
2018
|
+
return false;
|
|
2099
2019
|
if (type.startsWith("text/"))
|
|
2100
2020
|
return true;
|
|
2101
2021
|
if (type.endsWith("+json") || type.endsWith("+xml"))
|
|
@@ -2304,6 +2224,143 @@ CompressionModule = __decorateElement(_init, 0, "CompressionModule", _dec, Compr
|
|
|
2304
2224
|
__runInitializers(_init, 1, CompressionModule);
|
|
2305
2225
|
__decoratorMetadata(_init, CompressionModule);
|
|
2306
2226
|
let _CompressionModule = CompressionModule;
|
|
2227
|
+
// src/sse/event.ts
|
|
2228
|
+
var lines = (value) => value.split(/\r\n|\r|\n/);
|
|
2229
|
+
var oneLine = (value) => lines(value)[0] ?? "";
|
|
2230
|
+
var frameEvent = (event) => {
|
|
2231
|
+
const fields = [];
|
|
2232
|
+
if (event.event !== undefined)
|
|
2233
|
+
fields.push(`event: ${oneLine(event.event)}`);
|
|
2234
|
+
if (event.id !== undefined)
|
|
2235
|
+
fields.push(`id: ${oneLine(event.id)}`);
|
|
2236
|
+
if (event.retry !== undefined) {
|
|
2237
|
+
fields.push(`retry: ${Math.trunc(event.retry)}`);
|
|
2238
|
+
}
|
|
2239
|
+
if (event.data !== undefined) {
|
|
2240
|
+
const data = typeof event.data === "string" ? event.data : JSON.stringify(event.data);
|
|
2241
|
+
for (const line of lines(data ?? ""))
|
|
2242
|
+
fields.push(`data: ${line}`);
|
|
2243
|
+
}
|
|
2244
|
+
return `${fields.join(`
|
|
2245
|
+
`)}
|
|
2246
|
+
|
|
2247
|
+
`;
|
|
2248
|
+
};
|
|
2249
|
+
var frameComment = (text) => `${lines(text).map((line) => `:${line}`).join(`
|
|
2250
|
+
`)}
|
|
2251
|
+
|
|
2252
|
+
`;
|
|
2253
|
+
|
|
2254
|
+
// src/sse/stream.ts
|
|
2255
|
+
var DEFAULT_HEARTBEAT_MS = 15000;
|
|
2256
|
+
var encoder = new TextEncoder;
|
|
2257
|
+
var SSE_HEADERS = Object.freeze({
|
|
2258
|
+
"content-type": "text/event-stream",
|
|
2259
|
+
"cache-control": "no-cache, no-transform",
|
|
2260
|
+
connection: "keep-alive"
|
|
2261
|
+
});
|
|
2262
|
+
|
|
2263
|
+
class SseStream {
|
|
2264
|
+
#body;
|
|
2265
|
+
#controller;
|
|
2266
|
+
#timer;
|
|
2267
|
+
#lastEventId;
|
|
2268
|
+
#closed = false;
|
|
2269
|
+
constructor(options = {}) {
|
|
2270
|
+
this.#body = new ReadableStream({
|
|
2271
|
+
start: (controller) => {
|
|
2272
|
+
this.#controller = controller;
|
|
2273
|
+
},
|
|
2274
|
+
cancel: () => {
|
|
2275
|
+
this.#stop();
|
|
2276
|
+
}
|
|
2277
|
+
});
|
|
2278
|
+
this.comment();
|
|
2279
|
+
const heartbeatMs = options.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
2280
|
+
if (heartbeatMs > 0) {
|
|
2281
|
+
this.#timer = setInterval(() => {
|
|
2282
|
+
this.comment();
|
|
2283
|
+
}, heartbeatMs);
|
|
2284
|
+
this.#timer.unref();
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
static from(events, options = {}) {
|
|
2288
|
+
const stream = new SseStream(options);
|
|
2289
|
+
(async () => {
|
|
2290
|
+
try {
|
|
2291
|
+
for await (const event of events) {
|
|
2292
|
+
if (stream.#closed)
|
|
2293
|
+
break;
|
|
2294
|
+
stream.send(event);
|
|
2295
|
+
}
|
|
2296
|
+
stream.close();
|
|
2297
|
+
} catch (error) {
|
|
2298
|
+
stream.#fail(error);
|
|
2299
|
+
}
|
|
2300
|
+
})();
|
|
2301
|
+
return stream;
|
|
2302
|
+
}
|
|
2303
|
+
get lastEventId() {
|
|
2304
|
+
return this.#lastEventId;
|
|
2305
|
+
}
|
|
2306
|
+
get closed() {
|
|
2307
|
+
return this.#closed;
|
|
2308
|
+
}
|
|
2309
|
+
send(event) {
|
|
2310
|
+
if (event.id !== undefined)
|
|
2311
|
+
this.#lastEventId = event.id;
|
|
2312
|
+
this.#write(frameEvent(event));
|
|
2313
|
+
}
|
|
2314
|
+
comment(text = "") {
|
|
2315
|
+
this.#write(frameComment(text));
|
|
2316
|
+
}
|
|
2317
|
+
close() {
|
|
2318
|
+
if (this.#closed)
|
|
2319
|
+
return;
|
|
2320
|
+
this.#stop();
|
|
2321
|
+
this.#controller?.close();
|
|
2322
|
+
}
|
|
2323
|
+
toResponse(headers = {}) {
|
|
2324
|
+
return new Response(this.#body, {
|
|
2325
|
+
headers: { ...headers, ...SSE_HEADERS }
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
#write(text) {
|
|
2329
|
+
if (this.#closed)
|
|
2330
|
+
return;
|
|
2331
|
+
this.#controller?.enqueue(encoder.encode(text));
|
|
2332
|
+
}
|
|
2333
|
+
#fail(error) {
|
|
2334
|
+
if (this.#closed)
|
|
2335
|
+
return;
|
|
2336
|
+
this.#stop();
|
|
2337
|
+
this.#controller?.error(error);
|
|
2338
|
+
}
|
|
2339
|
+
#stop() {
|
|
2340
|
+
this.#closed = true;
|
|
2341
|
+
if (this.#timer !== undefined)
|
|
2342
|
+
clearInterval(this.#timer);
|
|
2343
|
+
this.#timer = undefined;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
Object.defineProperty(SseStream, Symbol.for("dunx.deps"), { value: () => [{ unresolved: "options: SseStreamOptions = {}", optional: true }] });
|
|
2347
|
+
|
|
2348
|
+
// src/sse/decorators.ts
|
|
2349
|
+
var LAST_EVENT_ID = "last-event-id";
|
|
2350
|
+
var respond = (result) => (result instanceof SseStream ? result : SseStream.from(result)).toResponse();
|
|
2351
|
+
var Sse = (path = "/", options) => (value, _context) => {
|
|
2352
|
+
const handler = value;
|
|
2353
|
+
function served(input) {
|
|
2354
|
+
const result = handler.call(this, {
|
|
2355
|
+
...input,
|
|
2356
|
+
lastEventId: input.req.headers.get(LAST_EVENT_ID) ?? undefined
|
|
2357
|
+
});
|
|
2358
|
+
return result instanceof Promise ? result.then((settled) => respond(settled)) : respond(result);
|
|
2359
|
+
}
|
|
2360
|
+
markRoute(served, { method: "GET", path, options });
|
|
2361
|
+
meta2(STREAMS2, true)(served);
|
|
2362
|
+
return served;
|
|
2363
|
+
};
|
|
2307
2364
|
// src/throttle/decorators.ts
|
|
2308
2365
|
var THROTTLE = metaKey2("throttle");
|
|
2309
2366
|
var SKIP_THROTTLE = metaKey2("skip-throttle");
|
|
@@ -2415,16 +2472,19 @@ class ThrottleGuard {
|
|
|
2415
2472
|
store;
|
|
2416
2473
|
address;
|
|
2417
2474
|
logger;
|
|
2475
|
+
claimed;
|
|
2418
2476
|
#warned = false;
|
|
2419
|
-
constructor(options, store, address, logger) {
|
|
2477
|
+
constructor(options, store, address, logger, claimed) {
|
|
2420
2478
|
this.options = options;
|
|
2421
2479
|
this.store = store;
|
|
2422
2480
|
this.address = address;
|
|
2423
2481
|
this.logger = logger;
|
|
2482
|
+
this.claimed = claimed;
|
|
2424
2483
|
}
|
|
2425
2484
|
async handle(req, ctx, next) {
|
|
2426
|
-
if (ctx.get(UNMATCHED2) === true)
|
|
2485
|
+
if (ctx.get(UNMATCHED2) === true && !this.claimed.has(ctx.path)) {
|
|
2427
2486
|
return next();
|
|
2487
|
+
}
|
|
2428
2488
|
if (ctx.get(SKIP_THROTTLE) === true)
|
|
2429
2489
|
return next();
|
|
2430
2490
|
const limit = ctx.get(THROTTLE) ?? this.options;
|
|
@@ -2477,7 +2537,7 @@ class ThrottleGuard {
|
|
|
2477
2537
|
this.logger.warn("The rate limiter is unreachable, so requests are not being counted.", { reason: error.message });
|
|
2478
2538
|
}
|
|
2479
2539
|
}
|
|
2480
|
-
Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), { value: () => [ThrottleOptions, ThrottleStore, ClientAddress, Logger5] });
|
|
2540
|
+
Object.defineProperty(ThrottleGuard, Symbol.for("dunx.deps"), { value: () => [ThrottleOptions, ThrottleStore, ClientAddress, Logger5, ClaimedRoutes] });
|
|
2481
2541
|
// src/throttle/module.ts
|
|
2482
2542
|
import {
|
|
2483
2543
|
Logger as Logger6,
|
|
@@ -2486,8 +2546,14 @@ import {
|
|
|
2486
2546
|
} from "@dunx/core";
|
|
2487
2547
|
var EXPORTS = [ThrottleOptions, ThrottleStore, ThrottleGuard];
|
|
2488
2548
|
var guard = () => provide4(ThrottleGuard, {
|
|
2489
|
-
useFactory: (options, store, address, logger) => new ThrottleGuard(options, store, address, logger),
|
|
2490
|
-
inject: [
|
|
2549
|
+
useFactory: (options, store, address, logger, claimed) => new ThrottleGuard(options, store, address, logger, claimed),
|
|
2550
|
+
inject: [
|
|
2551
|
+
ThrottleOptions,
|
|
2552
|
+
ThrottleStore,
|
|
2553
|
+
ClientAddress,
|
|
2554
|
+
Logger6,
|
|
2555
|
+
ClaimedRoutes
|
|
2556
|
+
]
|
|
2491
2557
|
});
|
|
2492
2558
|
var store = () => provide4(ThrottleStore, {
|
|
2493
2559
|
useFactory: (options) => options.store ?? new MemoryThrottleStore,
|
|
@@ -3259,11 +3325,14 @@ export {
|
|
|
3259
3325
|
RequestMetrics,
|
|
3260
3326
|
Roles2 as Roles,
|
|
3261
3327
|
SKIP_THROTTLE,
|
|
3328
|
+
STREAMS2 as STREAMS,
|
|
3262
3329
|
SkipThrottle,
|
|
3263
3330
|
SocketLoggingMiddleware,
|
|
3264
|
-
|
|
3331
|
+
Sse,
|
|
3332
|
+
SseStream,
|
|
3333
|
+
StaticFiles2 as StaticFiles,
|
|
3265
3334
|
StaticModule,
|
|
3266
|
-
StaticOptions,
|
|
3335
|
+
StaticOptions2 as StaticOptions,
|
|
3267
3336
|
THROTTLE,
|
|
3268
3337
|
TRACEPARENT_HEADER2 as TRACEPARENT_HEADER,
|
|
3269
3338
|
TRACERESPONSE_HEADER2 as TRACERESPONSE_HEADER,
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What the framework calls on itself, and the only place it is exported from
|
|
2
|
+
* What the framework calls on itself, and the only place it is exported from: the
|
|
3
|
+
* discovery readers and route metadata `@dunx/dashboard`, `@dunx/mcp`,
|
|
4
|
+
* `@dunx/openapi` and `@dunx/testing` import. The barrel stays the surface an app
|
|
5
|
+
* writes against.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
* package
|
|
6
|
-
*
|
|
7
|
-
* `@dunx/dashboard`, `@dunx/mcp`, `@dunx/openapi` and `@dunx/testing` import.
|
|
8
|
-
*
|
|
9
|
-
* It held 62 symbols and 50 had no importer, all of them reachable from inside
|
|
10
|
-
* this package by relative import. Add one back when a sibling needs it.
|
|
11
|
-
*
|
|
12
|
-
* No stability promise attaches to this subpath.
|
|
7
|
+
* It held 62 symbols and 50 had no importer, all reachable from inside this
|
|
8
|
+
* package by relative import. Add one back when a sibling needs it. No stability
|
|
9
|
+
* promise attaches here.
|
|
13
10
|
*/
|
|
14
11
|
export { discoverRoutes, joinPath, type DiscoveredRoute, } from './route/discover.js';
|
|
15
12
|
export { RoutePrefix } from './route/prefix.js';
|
|
16
13
|
export { defaultStatusFor } from './route/marker.js';
|
|
17
14
|
export { gatewaysOf, routesOf, type GatewayHandler, type GatewayNode, type RouteInputs, type RouteNode, } from './inspect.js';
|
|
18
15
|
export { buildContext } from './server/context.js';
|
|
16
|
+
export { IMMUTABLE_CACHE_CONTROL } from './static/files.js';
|
|
19
17
|
export { embedJson } from './server/html.js';
|
|
20
18
|
export { isGateway } from './ws/marker.js';
|