ingenium 0.0.1 → 0.0.2
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 +1 -1
- package/dist/index.cjs +268 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -9
- package/dist/index.d.ts +105 -9
- package/dist/index.js +268 -44
- package/dist/index.js.map +1 -1
- package/package.json +23 -8
- package/src/app.ts +7 -1
- package/src/body/multipart.ts +9 -1
- package/src/cors/middleware.ts +58 -3
- package/src/cors/types.ts +6 -3
- package/src/csrf/middleware.ts +98 -13
- package/src/csrf/types.ts +22 -1
- package/src/idempotency/middleware.ts +78 -5
- package/src/index.ts +1 -1
- package/src/jwt/middleware.ts +74 -3
- package/src/jwt/types.ts +12 -0
- package/src/jwt/verify.ts +75 -11
- package/src/problem/serialize.ts +18 -2
- package/src/proxy/trust.ts +22 -0
- package/src/session/middleware.ts +36 -3
- package/src/session/types.ts +14 -1
- package/src/static/middleware.ts +43 -8
- package/src/ws/index.ts +2 -0
- package/src/ws/middleware.ts +70 -0
- package/src/ws/types.ts +37 -0
package/dist/index.js
CHANGED
|
@@ -218,7 +218,10 @@ function parseMultipart(buffer, contentType, opts = {}) {
|
|
|
218
218
|
const maxFields = opts.maxFields ?? DEFAULT_MAX_FIELDS;
|
|
219
219
|
const allowed = opts.allowedMimePrefixes;
|
|
220
220
|
const boundary = extractBoundary(contentType);
|
|
221
|
-
const result = {
|
|
221
|
+
const result = {
|
|
222
|
+
fields: /* @__PURE__ */ Object.create(null),
|
|
223
|
+
files: /* @__PURE__ */ Object.create(null)
|
|
224
|
+
};
|
|
222
225
|
if (buffer.length === 0) return result;
|
|
223
226
|
const dashBoundary = Buffer$1.concat([DASH_DASH, Buffer$1.from(boundary)]);
|
|
224
227
|
let cursor = buffer.indexOf(dashBoundary);
|
|
@@ -645,6 +648,8 @@ function makeIngeniumCookies(ctx) {
|
|
|
645
648
|
}
|
|
646
649
|
|
|
647
650
|
// src/proxy/trust.ts
|
|
651
|
+
var IS_DEV = process.env.NODE_ENV !== "production";
|
|
652
|
+
var warnedTrustTrue = false;
|
|
648
653
|
function resolveForwarded(trust, remoteAddress, headers, defaultProtocol = "http") {
|
|
649
654
|
if (trust === false || trust === 0 || trust === void 0 || trust === null) {
|
|
650
655
|
return {
|
|
@@ -659,6 +664,16 @@ function resolveForwarded(trust, remoteAddress, headers, defaultProtocol = "http
|
|
|
659
664
|
const fullChain = [...xff, remoteAddress];
|
|
660
665
|
let trustedIp = remoteAddress;
|
|
661
666
|
if (typeof trust === "boolean" && trust === true) {
|
|
667
|
+
if (IS_DEV && !warnedTrustTrue) {
|
|
668
|
+
warnedTrustTrue = true;
|
|
669
|
+
try {
|
|
670
|
+
process.emitWarning(
|
|
671
|
+
"trustProxy: true fully trusts the client-supplied X-Forwarded-For header, so ctx.ip can be spoofed by any caller. This bypasses IP rate-limits/allowlists and poisons audit logs. For production, set trustProxy to a hop count (e.g. 1) or a CIDR/subnet trust list (e.g. ['loopback', '10.0.0.0/8']) so only your reverse proxy is trusted.",
|
|
672
|
+
{ code: "INGENIUM_TRUST_PROXY_TRUE" }
|
|
673
|
+
);
|
|
674
|
+
} catch {
|
|
675
|
+
}
|
|
676
|
+
}
|
|
662
677
|
trustedIp = fullChain[0] ?? remoteAddress;
|
|
663
678
|
} else if (typeof trust === "number") {
|
|
664
679
|
const idx = Math.max(0, fullChain.length - 1 - trust);
|
|
@@ -1081,7 +1096,7 @@ function respondJsonWithEtag(ctx, body, opts = {}) {
|
|
|
1081
1096
|
}
|
|
1082
1097
|
|
|
1083
1098
|
// src/context/context.ts
|
|
1084
|
-
var
|
|
1099
|
+
var IS_DEV2 = process.env.NODE_ENV !== "production";
|
|
1085
1100
|
var _trustProxyWarned = false;
|
|
1086
1101
|
var CRLF_RE = /[\r\n]/;
|
|
1087
1102
|
function assertHeaderNameSafe(name) {
|
|
@@ -1290,7 +1305,7 @@ var IngeniumContext = class {
|
|
|
1290
1305
|
this.headers,
|
|
1291
1306
|
this.baseProtocol
|
|
1292
1307
|
);
|
|
1293
|
-
if (
|
|
1308
|
+
if (IS_DEV2 && !_trustProxyWarned && this._trustProxy === false) {
|
|
1294
1309
|
if (this.headers["x-forwarded-for"] !== void 0) {
|
|
1295
1310
|
_trustProxyWarned = true;
|
|
1296
1311
|
try {
|
|
@@ -1363,7 +1378,7 @@ var IngeniumContext = class {
|
|
|
1363
1378
|
* eliminates the branch body behind the `IS_DEV` gate.
|
|
1364
1379
|
*/
|
|
1365
1380
|
_warnDoubleWrite(method) {
|
|
1366
|
-
if (!
|
|
1381
|
+
if (!IS_DEV2) return;
|
|
1367
1382
|
if (!this._written) return;
|
|
1368
1383
|
try {
|
|
1369
1384
|
process.emitWarning(
|
|
@@ -1573,7 +1588,7 @@ var IngeniumContextPool = class {
|
|
|
1573
1588
|
return this.pool.length;
|
|
1574
1589
|
}
|
|
1575
1590
|
};
|
|
1576
|
-
var
|
|
1591
|
+
var IS_DEV3 = process.env.NODE_ENV !== "production";
|
|
1577
1592
|
var _responseObjectWarned = false;
|
|
1578
1593
|
function reflectReturn(ctx, value) {
|
|
1579
1594
|
if (ctx._written) return;
|
|
@@ -1581,7 +1596,7 @@ function reflectReturn(ctx, value) {
|
|
|
1581
1596
|
ctx.status(204);
|
|
1582
1597
|
return;
|
|
1583
1598
|
}
|
|
1584
|
-
if (
|
|
1599
|
+
if (IS_DEV3 && typeof Response !== "undefined" && value instanceof Response) {
|
|
1585
1600
|
if (!_responseObjectWarned) {
|
|
1586
1601
|
_responseObjectWarned = true;
|
|
1587
1602
|
try {
|
|
@@ -3868,8 +3883,9 @@ var IngeniumApp = class {
|
|
|
3868
3883
|
throw miss;
|
|
3869
3884
|
}
|
|
3870
3885
|
const applicable = [...flat.globalMiddleware];
|
|
3886
|
+
const normalizedPath = ctx.path.includes("//") ? ctx.path.replace(/\/{2,}/g, "/") : ctx.path;
|
|
3871
3887
|
for (const scoped of flat.scopedMiddleware) {
|
|
3872
|
-
if (pathStartsWith(
|
|
3888
|
+
if (pathStartsWith(normalizedPath, scoped.prefix)) applicable.push(scoped.mw);
|
|
3873
3889
|
}
|
|
3874
3890
|
if (applicable.length === 0) {
|
|
3875
3891
|
throw miss;
|
|
@@ -4307,6 +4323,14 @@ function mimeFor(file) {
|
|
|
4307
4323
|
const ext = path.extname(file).slice(1).toLowerCase();
|
|
4308
4324
|
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
4309
4325
|
}
|
|
4326
|
+
function isUnderRoot(absRoot, target) {
|
|
4327
|
+
return target === absRoot || target.startsWith(absRoot + path.sep);
|
|
4328
|
+
}
|
|
4329
|
+
function hasDotfileSegment(absRoot, target) {
|
|
4330
|
+
const rel = path.relative(absRoot, target);
|
|
4331
|
+
if (rel.length === 0) return false;
|
|
4332
|
+
return rel.split(/[/\\]/).some((s) => s.length > 0 && s.startsWith("."));
|
|
4333
|
+
}
|
|
4310
4334
|
function makeEtag(stats) {
|
|
4311
4335
|
return `W/"${stats.size.toString(16)}-${Math.floor(stats.mtimeMs).toString(16)}"`;
|
|
4312
4336
|
}
|
|
@@ -4356,15 +4380,11 @@ function staticMiddleware(root, opts = {}) {
|
|
|
4356
4380
|
}
|
|
4357
4381
|
const joined = path.join(absRoot, urlPath);
|
|
4358
4382
|
const resolved = path.resolve(joined);
|
|
4359
|
-
|
|
4360
|
-
if (!isUnderRoot) {
|
|
4383
|
+
if (!isUnderRoot(absRoot, resolved)) {
|
|
4361
4384
|
ctx.status(403).text("Forbidden");
|
|
4362
4385
|
return;
|
|
4363
4386
|
}
|
|
4364
|
-
|
|
4365
|
-
const segments = rel.length === 0 ? [] : rel.split(/[/\\]/);
|
|
4366
|
-
const hasDot = segments.some((s) => s.length > 0 && s.startsWith("."));
|
|
4367
|
-
if (hasDot) {
|
|
4387
|
+
if (hasDotfileSegment(absRoot, resolved)) {
|
|
4368
4388
|
if (dotfiles === "deny") {
|
|
4369
4389
|
ctx.status(403).text("Forbidden");
|
|
4370
4390
|
return;
|
|
@@ -4412,6 +4432,19 @@ function staticMiddleware(root, opts = {}) {
|
|
|
4412
4432
|
if (!stats || !stats.isFile()) {
|
|
4413
4433
|
return next();
|
|
4414
4434
|
}
|
|
4435
|
+
if (!isUnderRoot(absRoot, target)) {
|
|
4436
|
+
ctx.status(403).text("Forbidden");
|
|
4437
|
+
return;
|
|
4438
|
+
}
|
|
4439
|
+
if (hasDotfileSegment(absRoot, target)) {
|
|
4440
|
+
if (dotfiles === "deny") {
|
|
4441
|
+
ctx.status(403).text("Forbidden");
|
|
4442
|
+
return;
|
|
4443
|
+
}
|
|
4444
|
+
if (dotfiles === "ignore") {
|
|
4445
|
+
return next();
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4415
4448
|
const etag = makeEtag(stats);
|
|
4416
4449
|
const lastModified = new Date(stats.mtimeMs).toUTCString();
|
|
4417
4450
|
ctx.set("etag", etag);
|
|
@@ -4470,6 +4503,7 @@ function staticMiddleware(root, opts = {}) {
|
|
|
4470
4503
|
}
|
|
4471
4504
|
|
|
4472
4505
|
// src/cors/middleware.ts
|
|
4506
|
+
var IS_DEV4 = process.env.NODE_ENV !== "production";
|
|
4473
4507
|
var DEFAULT_METHODS = [
|
|
4474
4508
|
"GET",
|
|
4475
4509
|
"HEAD",
|
|
@@ -4495,7 +4529,9 @@ async function resolveOrigin(spec, reqOrigin, ctx) {
|
|
|
4495
4529
|
if (typeof reqOrigin !== "string" || reqOrigin.length === 0) {
|
|
4496
4530
|
return { value: null, reflected: false };
|
|
4497
4531
|
}
|
|
4498
|
-
if (spec === true)
|
|
4532
|
+
if (spec === true) {
|
|
4533
|
+
return reqOrigin === "null" ? { value: null, reflected: true } : { value: reqOrigin, reflected: true };
|
|
4534
|
+
}
|
|
4499
4535
|
if (typeof spec === "string") {
|
|
4500
4536
|
return spec === reqOrigin ? { value: reqOrigin, reflected: true } : { value: null, reflected: true };
|
|
4501
4537
|
}
|
|
@@ -4525,10 +4561,29 @@ function corsMiddleware(opts = {}) {
|
|
|
4525
4561
|
const maxAge = opts.maxAge;
|
|
4526
4562
|
const optionsSuccessStatus = opts.optionsSuccessStatus ?? 204;
|
|
4527
4563
|
if (credentials && origin === "*") {
|
|
4528
|
-
throw new
|
|
4564
|
+
throw new IngeniumError(
|
|
4565
|
+
500,
|
|
4566
|
+
"CORS_CREDENTIALS_WILDCARD",
|
|
4529
4567
|
"ingenium.cors: `credentials: true` is incompatible with `origin: '*'`. Specify an explicit origin (string, array, regex, or function) instead."
|
|
4530
4568
|
);
|
|
4531
4569
|
}
|
|
4570
|
+
if (credentials && origin === true) {
|
|
4571
|
+
throw new IngeniumError(
|
|
4572
|
+
500,
|
|
4573
|
+
"CORS_CREDENTIALS_WILDCARD",
|
|
4574
|
+
"ingenium.cors: `credentials: true` is incompatible with `origin: true` (reflecting any Origin with credentials lets any site read authenticated responses). Specify an explicit allowlist (string, array, regex, or function)."
|
|
4575
|
+
);
|
|
4576
|
+
}
|
|
4577
|
+
if (IS_DEV4 && credentials && (typeof origin === "function" || origin instanceof RegExp)) {
|
|
4578
|
+
try {
|
|
4579
|
+
process.emitWarning(
|
|
4580
|
+
"ingenium.cors: `credentials: true` with a function/RegExp origin reflects the request Origin when the predicate matches. Ensure it never matches untrusted origins, or you expose authenticated responses to them.",
|
|
4581
|
+
{ code: "INGENIUM_CORS_CREDENTIALS_REFLECT" }
|
|
4582
|
+
);
|
|
4583
|
+
} catch {
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
const originIsFunction = typeof origin === "function";
|
|
4532
4587
|
const methodsHeader = methods.join(",");
|
|
4533
4588
|
const exposedHeader = exposedHeaders && exposedHeaders.length > 0 ? exposedHeaders.join(",") : void 0;
|
|
4534
4589
|
const allowedHeader = allowedHeaders && allowedHeaders.length > 0 ? allowedHeaders.join(",") : void 0;
|
|
@@ -4541,7 +4596,7 @@ function corsMiddleware(opts = {}) {
|
|
|
4541
4596
|
reqOriginStr,
|
|
4542
4597
|
ctx
|
|
4543
4598
|
);
|
|
4544
|
-
if (reflected) appendVary(ctx, "Origin");
|
|
4599
|
+
if (reflected || originIsFunction) appendVary(ctx, "Origin");
|
|
4545
4600
|
if (allowOrigin !== null) {
|
|
4546
4601
|
ctx.set("access-control-allow-origin", allowOrigin);
|
|
4547
4602
|
if (credentials) {
|
|
@@ -4754,6 +4809,8 @@ var IngeniumCsrfError = class extends IngeniumError {
|
|
|
4754
4809
|
super(403, "CSRF_FAILED", message);
|
|
4755
4810
|
}
|
|
4756
4811
|
};
|
|
4812
|
+
var IS_DEV5 = process.env.NODE_ENV !== "production";
|
|
4813
|
+
var CRLF_RE2 = /[\r\n]/;
|
|
4757
4814
|
var TOKEN_BYTES = 18;
|
|
4758
4815
|
var SAFE_METHODS_DEFAULT = ["GET", "HEAD", "OPTIONS", "TRACE"];
|
|
4759
4816
|
var COOKIE_NAME_DEFAULT = "ingenium.csrf";
|
|
@@ -4768,10 +4825,20 @@ function csrfMiddleware(opts = {}) {
|
|
|
4768
4825
|
await next();
|
|
4769
4826
|
return;
|
|
4770
4827
|
}
|
|
4771
|
-
|
|
4828
|
+
const binding = resolved.storage === "cookie" && resolved.sessionBinding ? resolved.sessionBinding(ctx) : void 0;
|
|
4829
|
+
if (IS_DEV5 && resolved.storage === "cookie" && resolved.secrets.length > 0 && binding === void 0) {
|
|
4830
|
+
try {
|
|
4831
|
+
process.emitWarning(
|
|
4832
|
+
"csrfMiddleware: cookie-mode token has no session binding. Double-submit without binding assumes no XSS and a strict SameSite cookie \u2014 any token the server mints is globally valid. Provide `sessionBinding` to tie the token to a session/user.",
|
|
4833
|
+
{ type: "IngeniumCsrfUnboundTokenWarning" }
|
|
4834
|
+
);
|
|
4835
|
+
} catch {
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
let expected = readExpectedToken(ctx, resolved, binding);
|
|
4772
4839
|
let mintedThisRequest = false;
|
|
4773
4840
|
if (!expected) {
|
|
4774
|
-
expected = mintToken(resolved);
|
|
4841
|
+
expected = mintToken(resolved, binding);
|
|
4775
4842
|
mintedThisRequest = true;
|
|
4776
4843
|
}
|
|
4777
4844
|
ctx.state.csrfToken = expected;
|
|
@@ -4791,14 +4858,15 @@ function csrfMiddleware(opts = {}) {
|
|
|
4791
4858
|
}
|
|
4792
4859
|
};
|
|
4793
4860
|
}
|
|
4794
|
-
function mintToken(opts) {
|
|
4861
|
+
function mintToken(opts, binding) {
|
|
4795
4862
|
const raw = randomBytes(TOKEN_BYTES).toString("base64url");
|
|
4796
4863
|
if (opts.storage === "session" || opts.secrets.length === 0) return raw;
|
|
4797
|
-
const sig = signToken(raw, opts.secrets[0]);
|
|
4864
|
+
const sig = signToken(raw, opts.secrets[0], binding);
|
|
4798
4865
|
return `${raw}.${sig}`;
|
|
4799
4866
|
}
|
|
4800
|
-
function signToken(raw, secret) {
|
|
4801
|
-
|
|
4867
|
+
function signToken(raw, secret, binding) {
|
|
4868
|
+
const message = binding === void 0 ? raw : `${raw}.${binding}`;
|
|
4869
|
+
return createHmac("sha256", secret).update(message).digest("base64url");
|
|
4802
4870
|
}
|
|
4803
4871
|
function tokenMatches(submitted, expected) {
|
|
4804
4872
|
const a = Buffer$1.from(submitted);
|
|
@@ -4806,24 +4874,24 @@ function tokenMatches(submitted, expected) {
|
|
|
4806
4874
|
if (a.length !== b.length) return false;
|
|
4807
4875
|
return timingSafeEqual(a, b);
|
|
4808
4876
|
}
|
|
4809
|
-
function verifySignedToken(token, secrets) {
|
|
4877
|
+
function verifySignedToken(token, secrets, binding) {
|
|
4810
4878
|
const dot = token.lastIndexOf(".");
|
|
4811
4879
|
if (dot <= 0) return false;
|
|
4812
4880
|
const raw = token.slice(0, dot);
|
|
4813
4881
|
const sig = token.slice(dot + 1);
|
|
4814
4882
|
for (const secret of secrets) {
|
|
4815
|
-
const expected = signToken(raw, secret);
|
|
4883
|
+
const expected = signToken(raw, secret, binding);
|
|
4816
4884
|
if (expected.length !== sig.length) continue;
|
|
4817
4885
|
if (timingSafeEqual(Buffer$1.from(expected), Buffer$1.from(sig))) return true;
|
|
4818
4886
|
}
|
|
4819
4887
|
return false;
|
|
4820
4888
|
}
|
|
4821
|
-
function readExpectedToken(ctx, opts) {
|
|
4889
|
+
function readExpectedToken(ctx, opts, binding) {
|
|
4822
4890
|
if (opts.storage === "cookie") {
|
|
4823
4891
|
const cookies = parseCookies(ctx.headers["cookie"]);
|
|
4824
4892
|
const token2 = cookies[opts.cookie.name];
|
|
4825
4893
|
if (!token2) return null;
|
|
4826
|
-
if (opts.secrets.length > 0 && !verifySignedToken(token2, opts.secrets)) return null;
|
|
4894
|
+
if (opts.secrets.length > 0 && !verifySignedToken(token2, opts.secrets, binding)) return null;
|
|
4827
4895
|
return token2;
|
|
4828
4896
|
}
|
|
4829
4897
|
const session = ctx.session;
|
|
@@ -4849,6 +4917,11 @@ function writeSession(ctx, token) {
|
|
|
4849
4917
|
session.set("csrfToken", token);
|
|
4850
4918
|
}
|
|
4851
4919
|
function appendSetCookie2(ctx, value) {
|
|
4920
|
+
if (CRLF_RE2.test(value)) {
|
|
4921
|
+
throw new IngeniumHeaderInjectionError(
|
|
4922
|
+
"csrfMiddleware: Set-Cookie value contains CR/LF (possible header injection)"
|
|
4923
|
+
);
|
|
4924
|
+
}
|
|
4852
4925
|
const existing = ctx._headers["set-cookie"];
|
|
4853
4926
|
if (!existing) {
|
|
4854
4927
|
ctx._headers["set-cookie"] = [value];
|
|
@@ -4884,13 +4957,37 @@ function resolveOptions(opts) {
|
|
|
4884
4957
|
path: opts.cookie?.path ?? "/",
|
|
4885
4958
|
domain: opts.cookie?.domain ?? "",
|
|
4886
4959
|
sameSite: opts.cookie?.sameSite ?? "lax",
|
|
4887
|
-
|
|
4960
|
+
// Secure-by-default: a plaintext CSRF cookie is readable by a network
|
|
4961
|
+
// attacker, who can then forge the double-submit header.
|
|
4962
|
+
secure: opts.cookie?.secure ?? true,
|
|
4888
4963
|
httpOnly: opts.cookie?.httpOnly ?? false,
|
|
4889
4964
|
maxAgeSeconds: opts.cookie?.maxAgeSeconds ?? 7 * 24 * 60 * 60
|
|
4890
4965
|
};
|
|
4966
|
+
if (CRLF_RE2.test(cookie.path) || CRLF_RE2.test(cookie.domain) || CRLF_RE2.test(cookie.name)) {
|
|
4967
|
+
throw new IngeniumHeaderInjectionError(
|
|
4968
|
+
"csrfMiddleware: cookie name/path/domain contains CR/LF (possible header injection)"
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4971
|
+
if (storage === "cookie" && cookie.secure === false && IS_DEV5) {
|
|
4972
|
+
try {
|
|
4973
|
+
process.emitWarning(
|
|
4974
|
+
"csrfMiddleware: CSRF cookie issued with Secure=false \u2014 the token is sent over plaintext HTTP and can be read by a network attacker. Only disable Secure for local HTTP development.",
|
|
4975
|
+
{ type: "IngeniumCsrfInsecureCookieWarning" }
|
|
4976
|
+
);
|
|
4977
|
+
} catch {
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4891
4980
|
const ignoreMethods = new Set((opts.ignoreMethods ?? SAFE_METHODS_DEFAULT).map((m) => m.toUpperCase()));
|
|
4892
4981
|
const value = opts.value ?? defaultValueReader;
|
|
4893
|
-
return {
|
|
4982
|
+
return {
|
|
4983
|
+
secrets,
|
|
4984
|
+
storage,
|
|
4985
|
+
cookie,
|
|
4986
|
+
ignoreMethods,
|
|
4987
|
+
value,
|
|
4988
|
+
skip: opts.skip ?? null,
|
|
4989
|
+
sessionBinding: opts.sessionBinding ?? null
|
|
4990
|
+
};
|
|
4894
4991
|
}
|
|
4895
4992
|
var defaultValueReader = (ctx) => {
|
|
4896
4993
|
for (const name of HEADER_NAMES_DEFAULT) {
|
|
@@ -4902,6 +4999,7 @@ var defaultValueReader = (ctx) => {
|
|
|
4902
4999
|
};
|
|
4903
5000
|
|
|
4904
5001
|
// src/problem/serialize.ts
|
|
5002
|
+
var IS_DEV6 = process.env.NODE_ENV !== "production";
|
|
4905
5003
|
var TITLES = Object.freeze({
|
|
4906
5004
|
NOT_FOUND: "Not Found",
|
|
4907
5005
|
UNAUTHORIZED: "Unauthorized",
|
|
@@ -4959,12 +5057,14 @@ function toProblemDetails(err, opts, ctx) {
|
|
|
4959
5057
|
}
|
|
4960
5058
|
return problem2;
|
|
4961
5059
|
}
|
|
5060
|
+
const exposeMessage = IS_DEV6 || opts.includeStack;
|
|
4962
5061
|
const message = err?.message;
|
|
5062
|
+
const detail = exposeMessage && typeof message === "string" && message.length > 0 ? message : "Internal Server Error";
|
|
4963
5063
|
const problem = {
|
|
4964
5064
|
type: "about:blank",
|
|
4965
5065
|
title: STATUS_REASON[500],
|
|
4966
5066
|
status: 500,
|
|
4967
|
-
detail
|
|
5067
|
+
detail
|
|
4968
5068
|
};
|
|
4969
5069
|
const instance = opts.instance(ctx);
|
|
4970
5070
|
if (instance !== void 0) problem.instance = instance;
|
|
@@ -5047,14 +5147,23 @@ var IdempotencyMemoryStore = class {
|
|
|
5047
5147
|
};
|
|
5048
5148
|
|
|
5049
5149
|
// src/idempotency/middleware.ts
|
|
5150
|
+
var IS_DEV7 = process.env.NODE_ENV !== "production";
|
|
5050
5151
|
var DEFAULT_METHODS2 = ["POST", "PATCH", "DELETE"];
|
|
5051
5152
|
var DEFAULT_CACHEABLE = (status) => status >= 200 && status < 500;
|
|
5153
|
+
var ANON_SCOPE = /* @__PURE__ */ Symbol("ingenium.idempotency.anon");
|
|
5052
5154
|
function defaultScope(ctx) {
|
|
5053
5155
|
const auth = ctx.headers["authorization"];
|
|
5054
5156
|
if (typeof auth === "string" && auth.length > 0) return auth;
|
|
5055
5157
|
if (Array.isArray(auth) && auth.length > 0 && typeof auth[0] === "string") return auth[0];
|
|
5056
|
-
return
|
|
5057
|
-
}
|
|
5158
|
+
return ANON_SCOPE;
|
|
5159
|
+
}
|
|
5160
|
+
var SENSITIVE_REPLAY_HEADERS = [
|
|
5161
|
+
"set-cookie",
|
|
5162
|
+
"authorization",
|
|
5163
|
+
"proxy-authorization",
|
|
5164
|
+
"www-authenticate",
|
|
5165
|
+
"proxy-authenticate"
|
|
5166
|
+
];
|
|
5058
5167
|
function readHeader2(ctx, lowerName) {
|
|
5059
5168
|
const v = ctx.headers[lowerName];
|
|
5060
5169
|
if (typeof v === "string") return v;
|
|
@@ -5092,6 +5201,7 @@ function replay(ctx, cached) {
|
|
|
5092
5201
|
for (const k of Object.keys(cached.headers)) {
|
|
5093
5202
|
const v = cached.headers[k];
|
|
5094
5203
|
if (v === void 0) continue;
|
|
5204
|
+
if (SENSITIVE_REPLAY_HEADERS.includes(k.toLowerCase())) continue;
|
|
5095
5205
|
ctx._headers[k] = Array.isArray(v) ? [...v] : v;
|
|
5096
5206
|
}
|
|
5097
5207
|
ctx._headers["idempotent-replayed"] = "true";
|
|
@@ -5107,17 +5217,20 @@ function replay(ctx, cached) {
|
|
|
5107
5217
|
ctx._written = true;
|
|
5108
5218
|
}
|
|
5109
5219
|
function idempotencyMiddleware(opts = {}) {
|
|
5220
|
+
const usingDefaultScope = opts.scope === void 0;
|
|
5221
|
+
const scopeFn = opts.scope ?? defaultScope;
|
|
5110
5222
|
const resolved = {
|
|
5111
5223
|
header: (opts.header ?? "Idempotency-Key").toLowerCase(),
|
|
5112
5224
|
store: opts.store ?? new IdempotencyMemoryStore(),
|
|
5113
5225
|
ttlMs: (opts.ttlSeconds ?? 86400) * 1e3,
|
|
5114
|
-
scope:
|
|
5226
|
+
scope: scopeFn,
|
|
5115
5227
|
methodSet: new Set(opts.methods ?? DEFAULT_METHODS2),
|
|
5116
5228
|
cacheable: opts.cacheable ?? DEFAULT_CACHEABLE
|
|
5117
5229
|
};
|
|
5118
5230
|
if (resolved.ttlMs <= 0) {
|
|
5119
5231
|
throw new Error("idempotency: ttlSeconds must be > 0");
|
|
5120
5232
|
}
|
|
5233
|
+
let anonBypassWarned = false;
|
|
5121
5234
|
const inflight = /* @__PURE__ */ new Map();
|
|
5122
5235
|
return async (ctx, next) => {
|
|
5123
5236
|
if (!resolved.methodSet.has(ctx.method)) {
|
|
@@ -5127,7 +5240,20 @@ function idempotencyMiddleware(opts = {}) {
|
|
|
5127
5240
|
if (!headerValue || headerValue.length === 0) {
|
|
5128
5241
|
return next();
|
|
5129
5242
|
}
|
|
5130
|
-
const scope =
|
|
5243
|
+
const scope = scopeFn(ctx);
|
|
5244
|
+
if (scope === ANON_SCOPE) {
|
|
5245
|
+
if (IS_DEV7 && usingDefaultScope && !anonBypassWarned) {
|
|
5246
|
+
anonBypassWarned = true;
|
|
5247
|
+
try {
|
|
5248
|
+
process.emitWarning(
|
|
5249
|
+
"idempotency: request to a cacheable route has no Authorization header, so the default scope cannot isolate clients. Idempotency caching was bypassed for this request to avoid serving one client the cached response of another. Supply an explicit `scope` function for unauthenticated endpoints.",
|
|
5250
|
+
{ type: "IngeniumIdempotencyWarning" }
|
|
5251
|
+
);
|
|
5252
|
+
} catch {
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
return next();
|
|
5256
|
+
}
|
|
5131
5257
|
const cacheKey = `${scope}:${ctx.method}:${ctx.path}:${headerValue}`;
|
|
5132
5258
|
const existing = await resolved.store.get(cacheKey);
|
|
5133
5259
|
if (existing) {
|
|
@@ -5192,11 +5318,23 @@ function decodeJsonSegment(segment) {
|
|
|
5192
5318
|
return null;
|
|
5193
5319
|
}
|
|
5194
5320
|
}
|
|
5321
|
+
function isSymmetricKey(key) {
|
|
5322
|
+
if (typeof key === "string") return !looksLikePem(key);
|
|
5323
|
+
if (Buffer$1.isBuffer(key)) return !looksLikePem(key.toString("latin1"));
|
|
5324
|
+
return key.type === "secret";
|
|
5325
|
+
}
|
|
5326
|
+
function looksLikePem(s) {
|
|
5327
|
+
return s.trimStart().startsWith("-----BEGIN");
|
|
5328
|
+
}
|
|
5195
5329
|
function hmacVerifies(digest, secret, signingInput, sig) {
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5330
|
+
try {
|
|
5331
|
+
const secretInput = typeof secret === "string" || Buffer$1.isBuffer(secret) ? secret : secret.export({ format: "buffer" });
|
|
5332
|
+
const expected = createHmac(digest, secretInput).update(signingInput).digest();
|
|
5333
|
+
if (sig.length !== expected.length) return false;
|
|
5334
|
+
return timingSafeEqual(sig, expected);
|
|
5335
|
+
} catch {
|
|
5336
|
+
return false;
|
|
5337
|
+
}
|
|
5200
5338
|
}
|
|
5201
5339
|
function asymmetricVerifies(spec, keyMaterial, signingInput, sig) {
|
|
5202
5340
|
let key;
|
|
@@ -5234,6 +5372,9 @@ function asymmetricVerifies(spec, keyMaterial, signingInput, sig) {
|
|
|
5234
5372
|
return false;
|
|
5235
5373
|
}
|
|
5236
5374
|
}
|
|
5375
|
+
function isFiniteNumber(v) {
|
|
5376
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
5377
|
+
}
|
|
5237
5378
|
function audienceMatches(claim, expected) {
|
|
5238
5379
|
const wanted = typeof expected === "string" ? [expected] : expected;
|
|
5239
5380
|
if (typeof claim === "string") return wanted.includes(claim);
|
|
@@ -5285,12 +5426,15 @@ function verifyJwt(token, keys, opts) {
|
|
|
5285
5426
|
}
|
|
5286
5427
|
let signatureOk = false;
|
|
5287
5428
|
for (const candidate of candidates) {
|
|
5429
|
+
const candidateIsSymmetric = isSymmetricKey(candidate);
|
|
5288
5430
|
if (spec.family === "hmac") {
|
|
5431
|
+
if (!candidateIsSymmetric) continue;
|
|
5289
5432
|
if (hmacVerifies(spec.digest, candidate, signingInput, sig)) {
|
|
5290
5433
|
signatureOk = true;
|
|
5291
5434
|
break;
|
|
5292
5435
|
}
|
|
5293
5436
|
} else {
|
|
5437
|
+
if (candidateIsSymmetric) continue;
|
|
5294
5438
|
if (asymmetricVerifies(spec, candidate, signingInput, sig)) {
|
|
5295
5439
|
signatureOk = true;
|
|
5296
5440
|
break;
|
|
@@ -5301,14 +5445,20 @@ function verifyJwt(token, keys, opts) {
|
|
|
5301
5445
|
const now = (opts.nowSeconds ?? (() => Math.floor(Date.now() / 1e3)))();
|
|
5302
5446
|
const skew = opts.clockSkewSeconds ?? 5;
|
|
5303
5447
|
const claims = payload;
|
|
5304
|
-
if (
|
|
5448
|
+
if ("exp" in claims && !isFiniteNumber(claims.exp)) return { error: "malformed" };
|
|
5449
|
+
if ("nbf" in claims && !isFiniteNumber(claims.nbf)) return { error: "malformed" };
|
|
5450
|
+
if ("iat" in claims && !isFiniteNumber(claims.iat)) return { error: "malformed" };
|
|
5451
|
+
const requireExp = opts.requireExp ?? true;
|
|
5452
|
+
if (isFiniteNumber(claims.exp)) {
|
|
5305
5453
|
if (claims.exp <= now - skew) return { error: "expired" };
|
|
5454
|
+
} else if (requireExp) {
|
|
5455
|
+
return { error: "missing_exp" };
|
|
5306
5456
|
}
|
|
5307
|
-
if (
|
|
5457
|
+
if (isFiniteNumber(claims.nbf)) {
|
|
5308
5458
|
if (claims.nbf > now + skew) return { error: "not_yet_valid" };
|
|
5309
5459
|
}
|
|
5310
5460
|
if (typeof opts.maxAgeSeconds === "number") {
|
|
5311
|
-
if (
|
|
5461
|
+
if (!isFiniteNumber(claims.iat)) return { error: "too_old" };
|
|
5312
5462
|
if (claims.iat + opts.maxAgeSeconds <= now - skew) return { error: "too_old" };
|
|
5313
5463
|
}
|
|
5314
5464
|
if (opts.audience !== void 0) {
|
|
@@ -5416,6 +5566,7 @@ async function doFetch(url) {
|
|
|
5416
5566
|
|
|
5417
5567
|
// src/jwt/middleware.ts
|
|
5418
5568
|
var DEFAULT_ALGORITHMS = ["HS256"];
|
|
5569
|
+
var DEFAULT_ASYMMETRIC_ALGORITHMS = ["RS256"];
|
|
5419
5570
|
var DEFAULT_JWKS_TTL_MS = 10 * 60 * 1e3;
|
|
5420
5571
|
var SUPPORTED = /* @__PURE__ */ new Set([
|
|
5421
5572
|
"HS256",
|
|
@@ -5431,6 +5582,14 @@ var SUPPORTED = /* @__PURE__ */ new Set([
|
|
|
5431
5582
|
"ES384",
|
|
5432
5583
|
"ES512"
|
|
5433
5584
|
]);
|
|
5585
|
+
var IngeniumJwtKeyAlgMismatchError = class extends IngeniumError {
|
|
5586
|
+
constructor(message) {
|
|
5587
|
+
super(500, "JWT_KEY_ALG_MISMATCH", message);
|
|
5588
|
+
}
|
|
5589
|
+
};
|
|
5590
|
+
function isHmacAlg(alg) {
|
|
5591
|
+
return alg === "HS256" || alg === "HS384" || alg === "HS512";
|
|
5592
|
+
}
|
|
5434
5593
|
var defaultGetToken = (ctx) => {
|
|
5435
5594
|
const raw = ctx.headers["authorization"];
|
|
5436
5595
|
if (!raw) return void 0;
|
|
@@ -5453,7 +5612,8 @@ function jwtMiddleware(opts) {
|
|
|
5453
5612
|
throw new Error("jwtMiddleware: `secret` (or `jwksUrl`) is required");
|
|
5454
5613
|
}
|
|
5455
5614
|
}
|
|
5456
|
-
const
|
|
5615
|
+
const defaultAlgorithms = hasJwks ? DEFAULT_ASYMMETRIC_ALGORITHMS : DEFAULT_ALGORITHMS;
|
|
5616
|
+
const algorithms = (opts.algorithms ?? defaultAlgorithms).slice();
|
|
5457
5617
|
if (algorithms.length === 0) {
|
|
5458
5618
|
throw new Error("jwtMiddleware: `algorithms` must contain at least one algorithm");
|
|
5459
5619
|
}
|
|
@@ -5467,6 +5627,7 @@ function jwtMiddleware(opts) {
|
|
|
5467
5627
|
}
|
|
5468
5628
|
const required = opts.required ?? true;
|
|
5469
5629
|
const clockSkewSeconds = opts.clockSkewSeconds ?? 5;
|
|
5630
|
+
const requireExp = opts.requireExp ?? true;
|
|
5470
5631
|
const jwksCacheMs = opts.jwksCacheMs ?? DEFAULT_JWKS_TTL_MS;
|
|
5471
5632
|
const jwksUrl = hasJwks ? opts.jwksUrl : null;
|
|
5472
5633
|
const getToken = opts.getToken ?? defaultGetToken;
|
|
@@ -5475,6 +5636,15 @@ function jwtMiddleware(opts) {
|
|
|
5475
5636
|
});
|
|
5476
5637
|
const staticKeys = opts.secret != null && typeof opts.secret !== "function" ? normaliseStaticKeys(opts.secret) : [];
|
|
5477
5638
|
const keyResolver = typeof opts.secret === "function" ? opts.secret : null;
|
|
5639
|
+
if (algorithms.some(isHmacAlg)) {
|
|
5640
|
+
for (const k of staticKeys) {
|
|
5641
|
+
if (staticKeyIsAsymmetric(k)) {
|
|
5642
|
+
throw new IngeniumJwtKeyAlgMismatchError(
|
|
5643
|
+
"jwtMiddleware: an HMAC algorithm (HS256/384/512) was paired with an asymmetric key (PEM or public/private KeyObject). This enables an algorithm-confusion forgery \u2014 use an asymmetric algorithm (RS*/PS*/ES*) for asymmetric keys, or a raw shared secret for HMAC."
|
|
5644
|
+
);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5478
5648
|
return async (ctx, next) => {
|
|
5479
5649
|
const token = await getToken(ctx);
|
|
5480
5650
|
if (!token) {
|
|
@@ -5504,7 +5674,7 @@ function jwtMiddleware(opts) {
|
|
|
5504
5674
|
logger({ reason: "no_keys_available" });
|
|
5505
5675
|
throw new IngeniumUnauthorizedError("Invalid token");
|
|
5506
5676
|
}
|
|
5507
|
-
const verifyOpts = { algorithms, clockSkewSeconds };
|
|
5677
|
+
const verifyOpts = { algorithms, clockSkewSeconds, requireExp };
|
|
5508
5678
|
if (opts.audience !== void 0) verifyOpts.audience = opts.audience;
|
|
5509
5679
|
if (opts.issuer !== void 0) verifyOpts.issuer = opts.issuer;
|
|
5510
5680
|
if (opts.maxAgeSeconds !== void 0) verifyOpts.maxAgeSeconds = opts.maxAgeSeconds;
|
|
@@ -5550,6 +5720,15 @@ function coerceJwtKey(k) {
|
|
|
5550
5720
|
}
|
|
5551
5721
|
throw new Error("jwtMiddleware: invalid `secret` entry \u2014 expected string, Buffer, KeyObject, or { kid, key }");
|
|
5552
5722
|
}
|
|
5723
|
+
function staticKeyIsAsymmetric(entry) {
|
|
5724
|
+
const key = typeof entry === "object" && entry !== null && !Buffer$1.isBuffer(entry) && "key" in entry ? entry.key : entry;
|
|
5725
|
+
if (typeof key === "string") return looksLikePem2(key);
|
|
5726
|
+
if (Buffer$1.isBuffer(key)) return looksLikePem2(key.toString("latin1"));
|
|
5727
|
+
return key.type === "public" || key.type === "private";
|
|
5728
|
+
}
|
|
5729
|
+
function looksLikePem2(s) {
|
|
5730
|
+
return s.trimStart().startsWith("-----BEGIN");
|
|
5731
|
+
}
|
|
5553
5732
|
function isKeyObject(v) {
|
|
5554
5733
|
if (typeof v !== "object" || v === null) return false;
|
|
5555
5734
|
const t = v.type;
|
|
@@ -6379,6 +6558,7 @@ var MemoryStore2 = class {
|
|
|
6379
6558
|
};
|
|
6380
6559
|
|
|
6381
6560
|
// src/session/middleware.ts
|
|
6561
|
+
var IS_DEV8 = process.env.NODE_ENV !== "production";
|
|
6382
6562
|
var DEFAULT_COOKIE_NAME = "ingenium.sid";
|
|
6383
6563
|
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
|
|
6384
6564
|
var ID_BYTES = 18;
|
|
@@ -6514,8 +6694,19 @@ function sessionMiddleware(opts) {
|
|
|
6514
6694
|
const cookieName = opts.cookieName ?? DEFAULT_COOKIE_NAME;
|
|
6515
6695
|
const maxAgeSeconds = opts.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
|
|
6516
6696
|
const rolling = opts.rolling ?? false;
|
|
6517
|
-
const
|
|
6697
|
+
const baseCookieOpts = opts.cookie ?? {};
|
|
6518
6698
|
const store = opts.store ?? new MemoryStore2();
|
|
6699
|
+
const resolvedSecure = baseCookieOpts.secure ?? !IS_DEV8;
|
|
6700
|
+
const cookieOpts = { ...baseCookieOpts, secure: resolvedSecure };
|
|
6701
|
+
if (!IS_DEV8 && resolvedSecure === false) {
|
|
6702
|
+
try {
|
|
6703
|
+
process.emitWarning(
|
|
6704
|
+
"ingenium: sessionMiddleware is issuing a session cookie WITHOUT the Secure attribute in production (cookie.secure === false). The cookie can be sent over plaintext HTTP and intercepted. Remove `cookie.secure: false` to use the safe production default, or terminate TLS in front of the app.",
|
|
6705
|
+
{ type: "IngeniumSessionInsecureCookieWarning" }
|
|
6706
|
+
);
|
|
6707
|
+
} catch {
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6519
6710
|
return async (ctx, next) => {
|
|
6520
6711
|
const cookies = parseCookieHeader2(ctx.headers.cookie);
|
|
6521
6712
|
const raw = cookies[cookieName];
|
|
@@ -6594,6 +6785,7 @@ async function commit(ctx, session, signingSecret, cookieName, maxAgeSeconds, ro
|
|
|
6594
6785
|
}
|
|
6595
6786
|
|
|
6596
6787
|
// src/ws/middleware.ts
|
|
6788
|
+
var IS_DEV9 = process.env.NODE_ENV !== "production";
|
|
6597
6789
|
async function peerHasWs() {
|
|
6598
6790
|
try {
|
|
6599
6791
|
await import('ws');
|
|
@@ -6612,6 +6804,14 @@ function createWebSocketRegistrar() {
|
|
|
6612
6804
|
if (routes.has(path2)) {
|
|
6613
6805
|
throw new Error(`ingenium.ws: path "${path2}" already has a WebSocket handler`);
|
|
6614
6806
|
}
|
|
6807
|
+
if (IS_DEV9 && options2.origin === void 0) {
|
|
6808
|
+
try {
|
|
6809
|
+
process.emitWarning(
|
|
6810
|
+
`ingenium.ws: WebSocket route "${path2}" registered without an \`origin\` option. WS handlers run outside the middleware pipeline and the browser sends the user's cookies on cross-origin upgrades \u2014 restrict the Origin (e.g. \`{ origin: true }\` for same-origin) or authenticate inside the handler to prevent Cross-Site WebSocket Hijacking (CSWSH).`
|
|
6811
|
+
);
|
|
6812
|
+
} catch {
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6615
6815
|
routes.set(path2, { path: path2, handler, options: options2 });
|
|
6616
6816
|
}
|
|
6617
6817
|
function attach(httpServer) {
|
|
@@ -6629,6 +6829,14 @@ function createWebSocketRegistrar() {
|
|
|
6629
6829
|
socket.destroy();
|
|
6630
6830
|
return;
|
|
6631
6831
|
}
|
|
6832
|
+
if (route.options.origin !== void 0) {
|
|
6833
|
+
const origin = req.headers.origin;
|
|
6834
|
+
if (!isOriginAllowed(route.options.origin, origin, req)) {
|
|
6835
|
+
socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
|
|
6836
|
+
socket.destroy();
|
|
6837
|
+
return;
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6632
6840
|
void (async () => {
|
|
6633
6841
|
try {
|
|
6634
6842
|
if (wsModule === null) wsModule = await import('ws');
|
|
@@ -6700,6 +6908,22 @@ function createWebSocketRegistrar() {
|
|
|
6700
6908
|
}
|
|
6701
6909
|
return { add, attach, close };
|
|
6702
6910
|
}
|
|
6911
|
+
function isOriginAllowed(policy, origin, req) {
|
|
6912
|
+
if (typeof policy === "function") return policy(origin, req);
|
|
6913
|
+
if (policy === false) return true;
|
|
6914
|
+
if (policy === true) {
|
|
6915
|
+
if (origin === void 0) return false;
|
|
6916
|
+
let originHost;
|
|
6917
|
+
try {
|
|
6918
|
+
originHost = new URL(origin).host;
|
|
6919
|
+
} catch {
|
|
6920
|
+
return false;
|
|
6921
|
+
}
|
|
6922
|
+
return originHost === req.headers.host;
|
|
6923
|
+
}
|
|
6924
|
+
if (origin === void 0) return false;
|
|
6925
|
+
return Array.isArray(policy) ? policy.includes(origin) : policy === origin;
|
|
6926
|
+
}
|
|
6703
6927
|
function buildMinimalContext(req, path2) {
|
|
6704
6928
|
const ctx = new IngeniumContext();
|
|
6705
6929
|
ctx.method = req.method ?? "GET";
|
|
@@ -6958,6 +7182,6 @@ var ingenium = Object.assign(ingeniumCore, {
|
|
|
6958
7182
|
});
|
|
6959
7183
|
var index_default = ingenium;
|
|
6960
7184
|
|
|
6961
|
-
export { CronRegistry, DecoratorRegistry, HTTP_METHODS, HooksRegistry, Http2Adapter, Http2cAdapter, IdempotencyMemoryStore, IngeniumApp, IngeniumBadRequestError, IngeniumBody, IngeniumContext, IngeniumContextPool, IngeniumCronJob, IngeniumCsrfError, IngeniumError, IngeniumHaltError, IngeniumHeaderInjectionError, IngeniumMethodNotAllowedError, IngeniumNotFoundError, IngeniumPayloadTooLargeError, IngeniumQueue, IngeniumTimeoutError, IngeniumUnauthorizedError, IngeniumUnserializableError, IngeniumValidationError, MemoryQueueStore, NodeAdapter, QueueRegistry, MemoryStore as RateLimitMemoryStore, RouteBuilder, Router, RouterTrie, ScopedApp, MemoryStore2 as SessionMemoryStore, TrieNode, WsNodeAdapter, _resetDefaultApp, accepts, acceptsCharsets, acceptsEncodings, acceptsLanguages, after, apiKeyMiddleware, before, clearJwksCache, compose, composeWithHandler, computeEtag, corsMiddleware as cors_, createWebSocketRegistrar, csrfMiddleware, index_default as default, defaultApp, del as delete, enableWebSockets, expandShorthand, fetchJwks, formatResponse, generateOpenApi, get, gracefulShutdown, head, idempotencyMiddleware, ingenium, isFresh, isStandardSchema, jwtMiddleware, listen, nextFireFrom, onError, openapiHandler, options, parseAcceptHeader, parseCronSpec, patch, peerHasWs, post, problemDetailsMiddleware, put, rateLimit, resolveForwarded, respondJsonWithEtag, safeJsonStringify, selectBest, sessionMiddleware, sortByPreference, sse, startKeepAlive, staticMiddleware as static_, toProblemDetails, use, verifyJwt };
|
|
7185
|
+
export { CronRegistry, DecoratorRegistry, HTTP_METHODS, HooksRegistry, Http2Adapter, Http2cAdapter, IdempotencyMemoryStore, IngeniumApp, IngeniumBadRequestError, IngeniumBody, IngeniumContext, IngeniumContextPool, IngeniumCronJob, IngeniumCsrfError, IngeniumError, IngeniumHaltError, IngeniumHeaderInjectionError, IngeniumJwtKeyAlgMismatchError, IngeniumMethodNotAllowedError, IngeniumNotFoundError, IngeniumPayloadTooLargeError, IngeniumQueue, IngeniumTimeoutError, IngeniumUnauthorizedError, IngeniumUnserializableError, IngeniumValidationError, MemoryQueueStore, NodeAdapter, QueueRegistry, MemoryStore as RateLimitMemoryStore, RouteBuilder, Router, RouterTrie, ScopedApp, MemoryStore2 as SessionMemoryStore, TrieNode, WsNodeAdapter, _resetDefaultApp, accepts, acceptsCharsets, acceptsEncodings, acceptsLanguages, after, apiKeyMiddleware, before, clearJwksCache, compose, composeWithHandler, computeEtag, corsMiddleware as cors_, createWebSocketRegistrar, csrfMiddleware, index_default as default, defaultApp, del as delete, enableWebSockets, expandShorthand, fetchJwks, formatResponse, generateOpenApi, get, gracefulShutdown, head, idempotencyMiddleware, ingenium, isFresh, isStandardSchema, jwtMiddleware, listen, nextFireFrom, onError, openapiHandler, options, parseAcceptHeader, parseCronSpec, patch, peerHasWs, post, problemDetailsMiddleware, put, rateLimit, resolveForwarded, respondJsonWithEtag, safeJsonStringify, selectBest, sessionMiddleware, sortByPreference, sse, startKeepAlive, staticMiddleware as static_, toProblemDetails, use, verifyJwt };
|
|
6962
7186
|
//# sourceMappingURL=index.js.map
|
|
6963
7187
|
//# sourceMappingURL=index.js.map
|