tina4-nodejs 3.13.90 → 3.13.92
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/CLAUDE.md +3 -3
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +72 -20
- package/packages/core/dist/index.js +72 -20
- package/packages/core/public/js/tina4-dev-admin.min.js +179 -179
- package/packages/core/src/auth.ts +141 -21
- package/packages/core/src/metrics.ts +53 -1
- package/packages/orm/dist/index.js +72 -20
package/CLAUDE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.
|
|
1
|
+
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.92)
|
|
2
2
|
|
|
3
3
|
> This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
|
|
4
4
|
|
|
5
5
|
## What This Project Is
|
|
6
6
|
|
|
7
|
-
Tina4 for Node.js/TypeScript v3.13.
|
|
7
|
+
Tina4 for Node.js/TypeScript v3.13.92 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
|
|
8
8
|
|
|
9
9
|
The philosophy: zero ceremony, batteries included, file system as source of truth.
|
|
10
10
|
|
|
@@ -1244,7 +1244,7 @@ When adding new features, add a corresponding `test/<feature>.test.ts` file.
|
|
|
1244
1244
|
## v3 Features Summary
|
|
1245
1245
|
|
|
1246
1246
|
- **98 built-in features**, zero third-party dependencies
|
|
1247
|
-
- **
|
|
1247
|
+
- **6,005 tests** passing across 191 files (build + typecheck green)
|
|
1248
1248
|
- **Race-safe `getNextId()`** with atomic sequence table (`tina4_sequences`) for SQLite/MySQL/MSSQL; PostgreSQL auto-creates sequences
|
|
1249
1249
|
- **Frond template engine optimizations**: pre-compiled regexes, lazy loop context (copy-on-write), filter chain caching, path split caching, inline common filters (11-15% speedup)
|
|
1250
1250
|
- **Production server auto-detect**: `npx tina4nodejs serve --production` auto-uses cluster mode
|
package/package.json
CHANGED
package/packages/cli/dist/bin.js
CHANGED
|
@@ -12122,6 +12122,7 @@ var init_logger = __esm({
|
|
|
12122
12122
|
var auth_exports = {};
|
|
12123
12123
|
__export(auth_exports, {
|
|
12124
12124
|
Auth: () => Auth,
|
|
12125
|
+
JWT_LEEWAY_SECONDS: () => JWT_LEEWAY_SECONDS,
|
|
12125
12126
|
authMiddleware: () => authMiddleware,
|
|
12126
12127
|
authenticateRequest: () => authenticateRequest,
|
|
12127
12128
|
checkPassword: () => checkPassword,
|
|
@@ -12130,6 +12131,7 @@ __export(auth_exports, {
|
|
|
12130
12131
|
getToken: () => getToken,
|
|
12131
12132
|
hashPassword: () => hashPassword,
|
|
12132
12133
|
refreshToken: () => refreshToken,
|
|
12134
|
+
resolveAlgorithm: () => resolveAlgorithm,
|
|
12133
12135
|
validToken: () => validToken,
|
|
12134
12136
|
validateApiKey: () => validateApiKey
|
|
12135
12137
|
});
|
|
@@ -12190,6 +12192,18 @@ function ensureDevSecret(cwd) {
|
|
|
12190
12192
|
}
|
|
12191
12193
|
return newSecret;
|
|
12192
12194
|
}
|
|
12195
|
+
function unsupportedAlgorithmError(algorithm) {
|
|
12196
|
+
return new Error(
|
|
12197
|
+
`Unsupported JWT algorithm "${algorithm}". Tina4 signs with ${SUPPORTED_ALGORITHMS.join(", ")} (HMAC via node:crypto; RS256 needs a PEM key pair). Set TINA4_JWT_ALGORITHM to one of those.`
|
|
12198
|
+
);
|
|
12199
|
+
}
|
|
12200
|
+
function resolveAlgorithm(algorithm) {
|
|
12201
|
+
const chosen = (algorithm || process.env.TINA4_JWT_ALGORITHM || "HS256").trim();
|
|
12202
|
+
if (!HMAC_DIGESTS.has(chosen) && !RSA_SIGN_ALGORITHMS.has(chosen)) {
|
|
12203
|
+
throw unsupportedAlgorithmError(chosen);
|
|
12204
|
+
}
|
|
12205
|
+
return chosen;
|
|
12206
|
+
}
|
|
12193
12207
|
function base64urlEncode(data) {
|
|
12194
12208
|
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
12195
12209
|
}
|
|
@@ -12212,7 +12226,7 @@ function getToken(payload, secretOrExpiresIn, expiresIn = 60, algorithm) {
|
|
|
12212
12226
|
if (!resolvedSecret) {
|
|
12213
12227
|
_warnBlankSecret();
|
|
12214
12228
|
}
|
|
12215
|
-
const resolvedAlgorithm = algorithm
|
|
12229
|
+
const resolvedAlgorithm = resolveAlgorithm(algorithm);
|
|
12216
12230
|
const header = { alg: resolvedAlgorithm, typ: "JWT" };
|
|
12217
12231
|
const now = Math.floor(Date.now() / 1e3);
|
|
12218
12232
|
const claims = { ...payload, iat: now };
|
|
@@ -12230,19 +12244,27 @@ function validToken(token, secret, algorithm) {
|
|
|
12230
12244
|
if (!resolvedSecret) {
|
|
12231
12245
|
_warnBlankSecret();
|
|
12232
12246
|
}
|
|
12233
|
-
const resolvedAlgorithm = algorithm
|
|
12247
|
+
const resolvedAlgorithm = resolveAlgorithm(algorithm);
|
|
12234
12248
|
try {
|
|
12235
12249
|
const parts = token.split(".");
|
|
12236
12250
|
if (parts.length !== 3) return null;
|
|
12237
12251
|
const [h, p, sig] = parts;
|
|
12252
|
+
const header = JSON.parse(base64urlDecode(h).toString());
|
|
12253
|
+
if (header.alg !== resolvedAlgorithm) return null;
|
|
12238
12254
|
const signingInput = `${h}.${p}`;
|
|
12239
12255
|
if (!verifySignature(signingInput, sig, resolvedSecret, resolvedAlgorithm)) {
|
|
12240
12256
|
return null;
|
|
12241
12257
|
}
|
|
12242
12258
|
const payload = JSON.parse(base64urlDecode(p).toString());
|
|
12243
|
-
|
|
12259
|
+
const now = Date.now() / 1e3;
|
|
12260
|
+
if (typeof payload.exp === "number" && now > payload.exp) {
|
|
12244
12261
|
return null;
|
|
12245
12262
|
}
|
|
12263
|
+
if (Object.hasOwn(payload, "nbf")) {
|
|
12264
|
+
const notBefore = payload.nbf;
|
|
12265
|
+
if (typeof notBefore !== "number" || !Number.isFinite(notBefore)) return null;
|
|
12266
|
+
if (now + JWT_LEEWAY_SECONDS < notBefore) return null;
|
|
12267
|
+
}
|
|
12246
12268
|
return payload;
|
|
12247
12269
|
} catch {
|
|
12248
12270
|
return null;
|
|
@@ -12258,32 +12280,33 @@ function getPayload(token) {
|
|
|
12258
12280
|
}
|
|
12259
12281
|
}
|
|
12260
12282
|
function sign(input, secret, algorithm) {
|
|
12261
|
-
|
|
12262
|
-
|
|
12263
|
-
return base64urlEncode(
|
|
12283
|
+
const digest = HMAC_DIGESTS.get(algorithm);
|
|
12284
|
+
if (digest) {
|
|
12285
|
+
return base64urlEncode(createHmac2(digest, secret).update(input).digest());
|
|
12264
12286
|
}
|
|
12265
|
-
|
|
12266
|
-
|
|
12287
|
+
const rsaAlgorithm = RSA_SIGN_ALGORITHMS.get(algorithm);
|
|
12288
|
+
if (rsaAlgorithm) {
|
|
12289
|
+
const signer = createSign(rsaAlgorithm);
|
|
12267
12290
|
signer.update(input);
|
|
12268
|
-
|
|
12269
|
-
return base64urlEncode(sig);
|
|
12291
|
+
return base64urlEncode(signer.sign(secret));
|
|
12270
12292
|
}
|
|
12271
|
-
throw
|
|
12293
|
+
throw unsupportedAlgorithmError(algorithm);
|
|
12272
12294
|
}
|
|
12273
12295
|
function verifySignature(input, sig, secret, algorithm) {
|
|
12274
|
-
if (algorithm
|
|
12296
|
+
if (HMAC_DIGESTS.has(algorithm)) {
|
|
12275
12297
|
const expected = sign(input, secret, algorithm);
|
|
12276
12298
|
const a = Buffer.from(sig);
|
|
12277
12299
|
const b = Buffer.from(expected);
|
|
12278
12300
|
if (a.length !== b.length) return false;
|
|
12279
12301
|
return timingSafeEqual(a, b);
|
|
12280
12302
|
}
|
|
12281
|
-
|
|
12282
|
-
|
|
12303
|
+
const rsaAlgorithm = RSA_SIGN_ALGORITHMS.get(algorithm);
|
|
12304
|
+
if (rsaAlgorithm) {
|
|
12305
|
+
const verifier = createVerify(rsaAlgorithm);
|
|
12283
12306
|
verifier.update(input);
|
|
12284
12307
|
return verifier.verify(secret, base64urlDecode(sig));
|
|
12285
12308
|
}
|
|
12286
|
-
throw
|
|
12309
|
+
throw unsupportedAlgorithmError(algorithm);
|
|
12287
12310
|
}
|
|
12288
12311
|
function hashPassword(password, salt, iterations = 26e4) {
|
|
12289
12312
|
const actualSalt = salt ?? randomBytes3(16).toString("hex");
|
|
@@ -12308,7 +12331,7 @@ function checkPassword(password, hash) {
|
|
|
12308
12331
|
return false;
|
|
12309
12332
|
}
|
|
12310
12333
|
}
|
|
12311
|
-
function authMiddleware(secret, algorithm
|
|
12334
|
+
function authMiddleware(secret, algorithm) {
|
|
12312
12335
|
return (req2, res, next) => {
|
|
12313
12336
|
const authHeader = req2.headers.authorization ?? "";
|
|
12314
12337
|
if (!authHeader.startsWith("Bearer ")) {
|
|
@@ -12331,11 +12354,11 @@ function refreshToken(token, expiresIn = 60) {
|
|
|
12331
12354
|
const { iat: _iat, exp: _exp, ...claims } = payload;
|
|
12332
12355
|
return getToken(claims, expiresIn);
|
|
12333
12356
|
}
|
|
12334
|
-
function authenticateRequest(headers, secret, algorithm
|
|
12357
|
+
function authenticateRequest(headers, secret, algorithm) {
|
|
12335
12358
|
const authHeader = headers.authorization ?? headers.Authorization ?? "";
|
|
12336
12359
|
if (!authHeader.startsWith("Bearer ")) return null;
|
|
12337
12360
|
const token = authHeader.slice(7);
|
|
12338
|
-
if (validToken(token)) return getPayload(token);
|
|
12361
|
+
if (validToken(token, secret, algorithm)) return getPayload(token);
|
|
12339
12362
|
if (validateApiKey(token)) {
|
|
12340
12363
|
return { _auth: "api_key" };
|
|
12341
12364
|
}
|
|
@@ -12349,12 +12372,23 @@ function validateApiKey(provided, expected) {
|
|
|
12349
12372
|
if (a.length !== b.length) return false;
|
|
12350
12373
|
return timingSafeEqual(a, b);
|
|
12351
12374
|
}
|
|
12352
|
-
var BLANK_SECRET_WARNING, Auth;
|
|
12375
|
+
var BLANK_SECRET_WARNING, HMAC_DIGESTS, RSA_SIGN_ALGORITHMS, SUPPORTED_ALGORITHMS, JWT_LEEWAY_SECONDS, Auth;
|
|
12353
12376
|
var init_auth = __esm({
|
|
12354
12377
|
"../core/src/auth.ts"() {
|
|
12355
12378
|
"use strict";
|
|
12356
12379
|
init_dotenv();
|
|
12357
12380
|
BLANK_SECRET_WARNING = "Auth: TINA4_SECRET is not set \u2014 JWT signing is insecure. Set TINA4_SECRET to a random value (e.g. `openssl rand -hex 32`) in your environment or .env before serving traffic. For LOCAL DEV, set TINA4_DEBUG=true and a per-machine secret is generated automatically into .env.local (gitignored). Seeing this warning means the run was NOT detected as dev \u2014 typically a container or CI without TINA4_DEBUG set, or TINA4_ENV=production.";
|
|
12381
|
+
HMAC_DIGESTS = /* @__PURE__ */ new Map([
|
|
12382
|
+
["HS256", "sha256"],
|
|
12383
|
+
["HS384", "sha384"],
|
|
12384
|
+
["HS512", "sha512"]
|
|
12385
|
+
]);
|
|
12386
|
+
RSA_SIGN_ALGORITHMS = /* @__PURE__ */ new Map([["RS256", "RSA-SHA256"]]);
|
|
12387
|
+
SUPPORTED_ALGORITHMS = [
|
|
12388
|
+
...HMAC_DIGESTS.keys(),
|
|
12389
|
+
...RSA_SIGN_ALGORITHMS.keys()
|
|
12390
|
+
];
|
|
12391
|
+
JWT_LEEWAY_SECONDS = 60;
|
|
12358
12392
|
Auth = class {
|
|
12359
12393
|
static getToken = getToken;
|
|
12360
12394
|
static validToken = validToken;
|
|
@@ -17938,7 +17972,7 @@ function extractFunctions(source, filePath, root = ".") {
|
|
|
17938
17972
|
if (funcName !== null && !NON_FUNCTION_WORDS.has(funcName)) {
|
|
17939
17973
|
const displayName = funcName === "constructor" && currentClass ? `${currentClass}.constructor` : currentClass !== null && !isTopLevelDecl ? `${currentClass}.${funcName}` : funcName;
|
|
17940
17974
|
const funcBody = extractFunctionBody(lines, i);
|
|
17941
|
-
const funcLoc =
|
|
17975
|
+
const funcLoc = Math.max(1, countLines(funcBody).loc);
|
|
17942
17976
|
const complexity = cycloMaticComplexity(funcBody);
|
|
17943
17977
|
const args = argsStr.split(",").map((a) => a.trim().split(":")[0].split("=")[0].replace("?", "").trim()).filter((a) => a && a !== "this");
|
|
17944
17978
|
functions.push({
|
|
@@ -17954,6 +17988,24 @@ function extractFunctions(source, filePath, root = ".") {
|
|
|
17954
17988
|
currentClass = null;
|
|
17955
17989
|
}
|
|
17956
17990
|
}
|
|
17991
|
+
return chargeNestedComplexityToTheNestedFunction(functions);
|
|
17992
|
+
}
|
|
17993
|
+
function chargeNestedComplexityToTheNestedFunction(functions) {
|
|
17994
|
+
if (functions.length < 2) return functions;
|
|
17995
|
+
const lastLine = (f) => f.line + Math.max(1, f.loc) - 1;
|
|
17996
|
+
const contains = (outer, inner) => inner.line > outer.line && lastLine(inner) <= lastLine(outer);
|
|
17997
|
+
const raw = functions.map((f) => f.complexity);
|
|
17998
|
+
functions.forEach((outer, i) => {
|
|
17999
|
+
let subtract = 0;
|
|
18000
|
+
functions.forEach((inner, j) => {
|
|
18001
|
+
if (i === j || !contains(outer, inner)) return;
|
|
18002
|
+
const nestedDeeper = functions.some(
|
|
18003
|
+
(mid, k) => k !== i && k !== j && contains(outer, mid) && contains(mid, inner)
|
|
18004
|
+
);
|
|
18005
|
+
if (!nestedDeeper) subtract += raw[j] - 1;
|
|
18006
|
+
});
|
|
18007
|
+
outer.complexity = Math.max(1, raw[i] - subtract);
|
|
18008
|
+
});
|
|
17957
18009
|
return functions;
|
|
17958
18010
|
}
|
|
17959
18011
|
function extractFunctionBody(lines, startLine) {
|
|
@@ -12121,6 +12121,7 @@ var init_logger = __esm({
|
|
|
12121
12121
|
var auth_exports = {};
|
|
12122
12122
|
__export(auth_exports, {
|
|
12123
12123
|
Auth: () => Auth,
|
|
12124
|
+
JWT_LEEWAY_SECONDS: () => JWT_LEEWAY_SECONDS,
|
|
12124
12125
|
authMiddleware: () => authMiddleware,
|
|
12125
12126
|
authenticateRequest: () => authenticateRequest,
|
|
12126
12127
|
checkPassword: () => checkPassword,
|
|
@@ -12129,6 +12130,7 @@ __export(auth_exports, {
|
|
|
12129
12130
|
getToken: () => getToken,
|
|
12130
12131
|
hashPassword: () => hashPassword,
|
|
12131
12132
|
refreshToken: () => refreshToken,
|
|
12133
|
+
resolveAlgorithm: () => resolveAlgorithm,
|
|
12132
12134
|
validToken: () => validToken,
|
|
12133
12135
|
validateApiKey: () => validateApiKey
|
|
12134
12136
|
});
|
|
@@ -12189,6 +12191,18 @@ function ensureDevSecret(cwd) {
|
|
|
12189
12191
|
}
|
|
12190
12192
|
return newSecret;
|
|
12191
12193
|
}
|
|
12194
|
+
function unsupportedAlgorithmError(algorithm) {
|
|
12195
|
+
return new Error(
|
|
12196
|
+
`Unsupported JWT algorithm "${algorithm}". Tina4 signs with ${SUPPORTED_ALGORITHMS.join(", ")} (HMAC via node:crypto; RS256 needs a PEM key pair). Set TINA4_JWT_ALGORITHM to one of those.`
|
|
12197
|
+
);
|
|
12198
|
+
}
|
|
12199
|
+
function resolveAlgorithm(algorithm) {
|
|
12200
|
+
const chosen = (algorithm || process.env.TINA4_JWT_ALGORITHM || "HS256").trim();
|
|
12201
|
+
if (!HMAC_DIGESTS.has(chosen) && !RSA_SIGN_ALGORITHMS.has(chosen)) {
|
|
12202
|
+
throw unsupportedAlgorithmError(chosen);
|
|
12203
|
+
}
|
|
12204
|
+
return chosen;
|
|
12205
|
+
}
|
|
12192
12206
|
function base64urlEncode(data) {
|
|
12193
12207
|
return data.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
12194
12208
|
}
|
|
@@ -12211,7 +12225,7 @@ function getToken(payload, secretOrExpiresIn, expiresIn = 60, algorithm) {
|
|
|
12211
12225
|
if (!resolvedSecret) {
|
|
12212
12226
|
_warnBlankSecret();
|
|
12213
12227
|
}
|
|
12214
|
-
const resolvedAlgorithm = algorithm
|
|
12228
|
+
const resolvedAlgorithm = resolveAlgorithm(algorithm);
|
|
12215
12229
|
const header = { alg: resolvedAlgorithm, typ: "JWT" };
|
|
12216
12230
|
const now = Math.floor(Date.now() / 1e3);
|
|
12217
12231
|
const claims = { ...payload, iat: now };
|
|
@@ -12229,19 +12243,27 @@ function validToken(token, secret, algorithm) {
|
|
|
12229
12243
|
if (!resolvedSecret) {
|
|
12230
12244
|
_warnBlankSecret();
|
|
12231
12245
|
}
|
|
12232
|
-
const resolvedAlgorithm = algorithm
|
|
12246
|
+
const resolvedAlgorithm = resolveAlgorithm(algorithm);
|
|
12233
12247
|
try {
|
|
12234
12248
|
const parts = token.split(".");
|
|
12235
12249
|
if (parts.length !== 3) return null;
|
|
12236
12250
|
const [h, p, sig] = parts;
|
|
12251
|
+
const header = JSON.parse(base64urlDecode(h).toString());
|
|
12252
|
+
if (header.alg !== resolvedAlgorithm) return null;
|
|
12237
12253
|
const signingInput = `${h}.${p}`;
|
|
12238
12254
|
if (!verifySignature(signingInput, sig, resolvedSecret, resolvedAlgorithm)) {
|
|
12239
12255
|
return null;
|
|
12240
12256
|
}
|
|
12241
12257
|
const payload = JSON.parse(base64urlDecode(p).toString());
|
|
12242
|
-
|
|
12258
|
+
const now = Date.now() / 1e3;
|
|
12259
|
+
if (typeof payload.exp === "number" && now > payload.exp) {
|
|
12243
12260
|
return null;
|
|
12244
12261
|
}
|
|
12262
|
+
if (Object.hasOwn(payload, "nbf")) {
|
|
12263
|
+
const notBefore = payload.nbf;
|
|
12264
|
+
if (typeof notBefore !== "number" || !Number.isFinite(notBefore)) return null;
|
|
12265
|
+
if (now + JWT_LEEWAY_SECONDS < notBefore) return null;
|
|
12266
|
+
}
|
|
12245
12267
|
return payload;
|
|
12246
12268
|
} catch {
|
|
12247
12269
|
return null;
|
|
@@ -12257,32 +12279,33 @@ function getPayload(token) {
|
|
|
12257
12279
|
}
|
|
12258
12280
|
}
|
|
12259
12281
|
function sign(input, secret, algorithm) {
|
|
12260
|
-
|
|
12261
|
-
|
|
12262
|
-
return base64urlEncode(
|
|
12282
|
+
const digest = HMAC_DIGESTS.get(algorithm);
|
|
12283
|
+
if (digest) {
|
|
12284
|
+
return base64urlEncode(createHmac2(digest, secret).update(input).digest());
|
|
12263
12285
|
}
|
|
12264
|
-
|
|
12265
|
-
|
|
12286
|
+
const rsaAlgorithm = RSA_SIGN_ALGORITHMS.get(algorithm);
|
|
12287
|
+
if (rsaAlgorithm) {
|
|
12288
|
+
const signer = createSign(rsaAlgorithm);
|
|
12266
12289
|
signer.update(input);
|
|
12267
|
-
|
|
12268
|
-
return base64urlEncode(sig);
|
|
12290
|
+
return base64urlEncode(signer.sign(secret));
|
|
12269
12291
|
}
|
|
12270
|
-
throw
|
|
12292
|
+
throw unsupportedAlgorithmError(algorithm);
|
|
12271
12293
|
}
|
|
12272
12294
|
function verifySignature(input, sig, secret, algorithm) {
|
|
12273
|
-
if (algorithm
|
|
12295
|
+
if (HMAC_DIGESTS.has(algorithm)) {
|
|
12274
12296
|
const expected = sign(input, secret, algorithm);
|
|
12275
12297
|
const a = Buffer.from(sig);
|
|
12276
12298
|
const b = Buffer.from(expected);
|
|
12277
12299
|
if (a.length !== b.length) return false;
|
|
12278
12300
|
return timingSafeEqual(a, b);
|
|
12279
12301
|
}
|
|
12280
|
-
|
|
12281
|
-
|
|
12302
|
+
const rsaAlgorithm = RSA_SIGN_ALGORITHMS.get(algorithm);
|
|
12303
|
+
if (rsaAlgorithm) {
|
|
12304
|
+
const verifier = createVerify(rsaAlgorithm);
|
|
12282
12305
|
verifier.update(input);
|
|
12283
12306
|
return verifier.verify(secret, base64urlDecode(sig));
|
|
12284
12307
|
}
|
|
12285
|
-
throw
|
|
12308
|
+
throw unsupportedAlgorithmError(algorithm);
|
|
12286
12309
|
}
|
|
12287
12310
|
function hashPassword(password, salt, iterations = 26e4) {
|
|
12288
12311
|
const actualSalt = salt ?? randomBytes3(16).toString("hex");
|
|
@@ -12307,7 +12330,7 @@ function checkPassword(password, hash) {
|
|
|
12307
12330
|
return false;
|
|
12308
12331
|
}
|
|
12309
12332
|
}
|
|
12310
|
-
function authMiddleware(secret, algorithm
|
|
12333
|
+
function authMiddleware(secret, algorithm) {
|
|
12311
12334
|
return (req2, res, next) => {
|
|
12312
12335
|
const authHeader = req2.headers.authorization ?? "";
|
|
12313
12336
|
if (!authHeader.startsWith("Bearer ")) {
|
|
@@ -12330,11 +12353,11 @@ function refreshToken(token, expiresIn = 60) {
|
|
|
12330
12353
|
const { iat: _iat, exp: _exp, ...claims } = payload;
|
|
12331
12354
|
return getToken(claims, expiresIn);
|
|
12332
12355
|
}
|
|
12333
|
-
function authenticateRequest(headers, secret, algorithm
|
|
12356
|
+
function authenticateRequest(headers, secret, algorithm) {
|
|
12334
12357
|
const authHeader = headers.authorization ?? headers.Authorization ?? "";
|
|
12335
12358
|
if (!authHeader.startsWith("Bearer ")) return null;
|
|
12336
12359
|
const token = authHeader.slice(7);
|
|
12337
|
-
if (validToken(token)) return getPayload(token);
|
|
12360
|
+
if (validToken(token, secret, algorithm)) return getPayload(token);
|
|
12338
12361
|
if (validateApiKey(token)) {
|
|
12339
12362
|
return { _auth: "api_key" };
|
|
12340
12363
|
}
|
|
@@ -12348,12 +12371,23 @@ function validateApiKey(provided, expected) {
|
|
|
12348
12371
|
if (a.length !== b.length) return false;
|
|
12349
12372
|
return timingSafeEqual(a, b);
|
|
12350
12373
|
}
|
|
12351
|
-
var BLANK_SECRET_WARNING, Auth;
|
|
12374
|
+
var BLANK_SECRET_WARNING, HMAC_DIGESTS, RSA_SIGN_ALGORITHMS, SUPPORTED_ALGORITHMS, JWT_LEEWAY_SECONDS, Auth;
|
|
12352
12375
|
var init_auth = __esm({
|
|
12353
12376
|
"src/auth.ts"() {
|
|
12354
12377
|
"use strict";
|
|
12355
12378
|
init_dotenv();
|
|
12356
12379
|
BLANK_SECRET_WARNING = "Auth: TINA4_SECRET is not set \u2014 JWT signing is insecure. Set TINA4_SECRET to a random value (e.g. `openssl rand -hex 32`) in your environment or .env before serving traffic. For LOCAL DEV, set TINA4_DEBUG=true and a per-machine secret is generated automatically into .env.local (gitignored). Seeing this warning means the run was NOT detected as dev \u2014 typically a container or CI without TINA4_DEBUG set, or TINA4_ENV=production.";
|
|
12380
|
+
HMAC_DIGESTS = /* @__PURE__ */ new Map([
|
|
12381
|
+
["HS256", "sha256"],
|
|
12382
|
+
["HS384", "sha384"],
|
|
12383
|
+
["HS512", "sha512"]
|
|
12384
|
+
]);
|
|
12385
|
+
RSA_SIGN_ALGORITHMS = /* @__PURE__ */ new Map([["RS256", "RSA-SHA256"]]);
|
|
12386
|
+
SUPPORTED_ALGORITHMS = [
|
|
12387
|
+
...HMAC_DIGESTS.keys(),
|
|
12388
|
+
...RSA_SIGN_ALGORITHMS.keys()
|
|
12389
|
+
];
|
|
12390
|
+
JWT_LEEWAY_SECONDS = 60;
|
|
12357
12391
|
Auth = class {
|
|
12358
12392
|
static getToken = getToken;
|
|
12359
12393
|
static validToken = validToken;
|
|
@@ -17937,7 +17971,7 @@ function extractFunctions(source, filePath, root = ".") {
|
|
|
17937
17971
|
if (funcName !== null && !NON_FUNCTION_WORDS.has(funcName)) {
|
|
17938
17972
|
const displayName = funcName === "constructor" && currentClass ? `${currentClass}.constructor` : currentClass !== null && !isTopLevelDecl ? `${currentClass}.${funcName}` : funcName;
|
|
17939
17973
|
const funcBody = extractFunctionBody(lines, i);
|
|
17940
|
-
const funcLoc =
|
|
17974
|
+
const funcLoc = Math.max(1, countLines(funcBody).loc);
|
|
17941
17975
|
const complexity = cycloMaticComplexity(funcBody);
|
|
17942
17976
|
const args = argsStr.split(",").map((a) => a.trim().split(":")[0].split("=")[0].replace("?", "").trim()).filter((a) => a && a !== "this");
|
|
17943
17977
|
functions.push({
|
|
@@ -17953,6 +17987,24 @@ function extractFunctions(source, filePath, root = ".") {
|
|
|
17953
17987
|
currentClass = null;
|
|
17954
17988
|
}
|
|
17955
17989
|
}
|
|
17990
|
+
return chargeNestedComplexityToTheNestedFunction(functions);
|
|
17991
|
+
}
|
|
17992
|
+
function chargeNestedComplexityToTheNestedFunction(functions) {
|
|
17993
|
+
if (functions.length < 2) return functions;
|
|
17994
|
+
const lastLine = (f) => f.line + Math.max(1, f.loc) - 1;
|
|
17995
|
+
const contains = (outer, inner) => inner.line > outer.line && lastLine(inner) <= lastLine(outer);
|
|
17996
|
+
const raw = functions.map((f) => f.complexity);
|
|
17997
|
+
functions.forEach((outer, i) => {
|
|
17998
|
+
let subtract = 0;
|
|
17999
|
+
functions.forEach((inner, j) => {
|
|
18000
|
+
if (i === j || !contains(outer, inner)) return;
|
|
18001
|
+
const nestedDeeper = functions.some(
|
|
18002
|
+
(mid, k) => k !== i && k !== j && contains(outer, mid) && contains(mid, inner)
|
|
18003
|
+
);
|
|
18004
|
+
if (!nestedDeeper) subtract += raw[j] - 1;
|
|
18005
|
+
});
|
|
18006
|
+
outer.complexity = Math.max(1, raw[i] - subtract);
|
|
18007
|
+
});
|
|
17956
18008
|
return functions;
|
|
17957
18009
|
}
|
|
17958
18010
|
function extractFunctionBody(lines, startLine) {
|