@getstrata/core 1.0.2 → 1.0.4
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/CHANGELOG.md +16 -0
- package/README.md +1 -1
- package/dist/core/database/index.d.ts +2 -1
- package/dist/core/database/mysqlConnection.d.ts +8 -4
- package/dist/core/runtime/appEnv.d.ts +16 -2
- package/dist/core/runtime/appKeyPrefix.d.ts +8 -1
- package/dist/core/runtime/optionalPeer.d.ts +2 -0
- package/dist/core/view/etaViewEngine.d.ts +5 -1
- package/dist/entries/audit/exportAuditLogs.js +83 -18
- package/dist/entries/audit/siemFormatter.js +32 -3
- package/dist/entries/auth/intendedUrlCookie.js +33 -4
- package/dist/entries/auth/jwt.js +36 -4
- package/dist/entries/auth/jwtGuard.js +36 -4
- package/dist/entries/auth/oauth/providers.js +32 -3
- package/dist/entries/auth/oauth/samlProvider.js +32 -3
- package/dist/entries/auth/passwordConfirmCookie.js +35 -6
- package/dist/entries/auth/sessionCookie.js +35 -6
- package/dist/entries/auth/sessionGuard.js +35 -6
- package/dist/entries/auth/tokenHash.js +33 -4
- package/dist/entries/cache/createCacheStore.js +32 -3
- package/dist/entries/database/mysqlConnection.js +86 -5
- package/dist/entries/facades.js +32 -3
- package/dist/entries/http/corsMiddleware.js +16 -1
- package/dist/entries/http/csrfMiddleware.js +34 -5
- package/dist/entries/http/csrfToken.js +34 -5
- package/dist/entries/http/flashMiddleware.js +33 -4
- package/dist/entries/http/flashSession.js +33 -4
- package/dist/entries/http/loginThrottleMiddleware.js +1 -373
- package/dist/entries/http/memoryThrottleMiddleware.js +32 -58
- package/dist/entries/http/response.js +16 -56
- package/dist/entries/http/scimThrottleMiddleware.js +32 -3
- package/dist/entries/http/securityHeadersMiddleware.js +32 -3
- package/dist/entries/http/signedUrl.js +33 -4
- package/dist/entries/http/throttleMiddleware.js +32 -58
- package/dist/entries/http/webErrorResponse.js +1 -401
- package/dist/entries/jobs/exportAuditLogsJob.js +83 -18
- package/dist/entries/lifecycle/gracefulShutdown.js +1 -50
- package/dist/entries/mail/mailer.js +32 -3
- package/dist/entries/openapi/generator.js +32 -3
- package/dist/entries/queue/createAppQueue.js +32 -3
- package/dist/entries/queue/publicQueue.js +32 -3
- package/dist/entries/queue/queueMetrics.js +32 -3
- package/dist/entries/queue/redisQueue.js +32 -3
- package/dist/entries/runtime/appEnv.js +18 -1
- package/dist/entries/runtime/appKeyPrefix.js +1 -70
- package/dist/entries/security/oauthState.js +33 -4
- package/dist/entries/security/safeFetch.js +32 -9
- package/dist/entries/security/safeUrl.js +1 -105
- package/dist/entries/security/totp.js +32 -3
- package/dist/entries/tenant/databaseTenantContext.js +48 -6
- package/dist/entries/tracing/tracingMiddleware.js +32 -3
- package/dist/entries/view.js +1 -748
- package/dist/framework/public-api.d.ts +28 -18
- package/dist/index.js +555 -68
- package/package.json +12 -6
package/dist/index.js
CHANGED
|
@@ -490,6 +490,26 @@ class AuthManager {
|
|
|
490
490
|
// ../../src/core/auth/jwt.ts
|
|
491
491
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
492
492
|
|
|
493
|
+
// ../../src/core/runtime/appEnv.ts
|
|
494
|
+
var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
|
|
495
|
+
function normalizeEnvValue(value) {
|
|
496
|
+
return (value ?? "").trim().toLowerCase();
|
|
497
|
+
}
|
|
498
|
+
function isProductionEnv(env = process.env) {
|
|
499
|
+
const appEnv = normalizeEnvValue(env.APP_ENV);
|
|
500
|
+
const nodeEnv = normalizeEnvValue(env.NODE_ENV);
|
|
501
|
+
if (appEnv === "production" || nodeEnv === "production") {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
if (appEnv === "") {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
return !NON_PRODUCTION_APP_ENVS.has(appEnv);
|
|
508
|
+
}
|
|
509
|
+
function envFlagEnabled(value) {
|
|
510
|
+
return value === "true";
|
|
511
|
+
}
|
|
512
|
+
|
|
493
513
|
// ../../src/core/runtime/appKeyPrefix.ts
|
|
494
514
|
function appKeyPrefix() {
|
|
495
515
|
return process.env.APP_KEY_PREFIX?.trim() || "strata";
|
|
@@ -500,6 +520,18 @@ function appCookieName(kind) {
|
|
|
500
520
|
function appDevSecret(kind) {
|
|
501
521
|
return `${appKeyPrefix()}-dev-${kind}`;
|
|
502
522
|
}
|
|
523
|
+
function requireConfiguredSecret(names, devKind, env = process.env) {
|
|
524
|
+
for (const name of names) {
|
|
525
|
+
const value = env[name]?.trim();
|
|
526
|
+
if (value) {
|
|
527
|
+
return value;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (isProductionEnv(env)) {
|
|
531
|
+
throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
|
|
532
|
+
}
|
|
533
|
+
return appDevSecret(devKind);
|
|
534
|
+
}
|
|
503
535
|
function namespacedRedisKey(kind) {
|
|
504
536
|
return `${appKeyPrefix()}:${kind}`;
|
|
505
537
|
}
|
|
@@ -508,16 +540,48 @@ function smtpEhloHost() {
|
|
|
508
540
|
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
509
541
|
return safe || "strata.local";
|
|
510
542
|
}
|
|
543
|
+
function siemEventType() {
|
|
544
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
545
|
+
}
|
|
546
|
+
function appUserAgent() {
|
|
547
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
548
|
+
}
|
|
511
549
|
function otelServiceName() {
|
|
512
550
|
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
513
551
|
}
|
|
552
|
+
function webhookSignatureHeader() {
|
|
553
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
554
|
+
}
|
|
555
|
+
function appDisplayName() {
|
|
556
|
+
return process.env.APP_NAME?.trim() || "Strata";
|
|
557
|
+
}
|
|
514
558
|
function appEnv() {
|
|
515
559
|
return process.env.APP_ENV?.trim() || "local";
|
|
516
560
|
}
|
|
561
|
+
function appUrl() {
|
|
562
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
563
|
+
}
|
|
564
|
+
function apiPrefix() {
|
|
565
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
566
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
567
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
568
|
+
return trimmed || "/api/v1";
|
|
569
|
+
}
|
|
570
|
+
function sdkClientClassName() {
|
|
571
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
572
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
573
|
+
return override;
|
|
574
|
+
}
|
|
575
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
576
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
577
|
+
}
|
|
517
578
|
|
|
518
579
|
// ../../src/core/auth/jwt.ts
|
|
519
580
|
function resolveJwtSecret(secret) {
|
|
520
|
-
|
|
581
|
+
if (secret?.trim()) {
|
|
582
|
+
return secret.trim();
|
|
583
|
+
}
|
|
584
|
+
return requireConfiguredSecret(["JWT_SECRET", "SESSION_SECRET"], "jwt-secret");
|
|
521
585
|
}
|
|
522
586
|
function jwtTtlSeconds(override) {
|
|
523
587
|
if (typeof override === "number" && Number.isInteger(override) && override > 0) {
|
|
@@ -642,6 +706,9 @@ function configureMembershipLookup(lookup) {
|
|
|
642
706
|
function resolveMembershipLookup() {
|
|
643
707
|
return membershipRepository;
|
|
644
708
|
}
|
|
709
|
+
function resetMembershipLookupForTests() {
|
|
710
|
+
membershipRepository = uninitializedMembershipLookup;
|
|
711
|
+
}
|
|
645
712
|
async function runWithMembershipContext(callback) {
|
|
646
713
|
const user = currentAuthUser();
|
|
647
714
|
if (!user || isGlobalAdmin(user)) {
|
|
@@ -1101,6 +1168,11 @@ function getDefaultDatabaseQuery() {
|
|
|
1101
1168
|
}
|
|
1102
1169
|
return query;
|
|
1103
1170
|
}
|
|
1171
|
+
function resetDefaultDatabasePoolForTests() {
|
|
1172
|
+
const holder = defaultConnectionState();
|
|
1173
|
+
holder.pool = null;
|
|
1174
|
+
holder.query = null;
|
|
1175
|
+
}
|
|
1104
1176
|
|
|
1105
1177
|
// ../../src/core/security/timingSafeCompare.ts
|
|
1106
1178
|
import { timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
@@ -2215,6 +2287,14 @@ function appendWhereParts(tableName, where, params) {
|
|
|
2215
2287
|
}
|
|
2216
2288
|
return clauses.join(" AND ");
|
|
2217
2289
|
}
|
|
2290
|
+
function buildWhereClause(tableName, where = {}) {
|
|
2291
|
+
const params = [];
|
|
2292
|
+
const body = appendWhereParts(tableName, where, params);
|
|
2293
|
+
return {
|
|
2294
|
+
clause: body.length > 0 ? ` WHERE ${body}` : "",
|
|
2295
|
+
params
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2218
2298
|
function remapExistsSql(sql, existsParams, params) {
|
|
2219
2299
|
const offset = params.length;
|
|
2220
2300
|
params.push(...existsParams);
|
|
@@ -5211,8 +5291,55 @@ function registerModelRepository(model, repository) {
|
|
|
5211
5291
|
ensureBooted(model);
|
|
5212
5292
|
return model;
|
|
5213
5293
|
}
|
|
5294
|
+
// ../../src/core/runtime/optionalPeer.ts
|
|
5295
|
+
function missingOptionalPeer(packageName, reason, error) {
|
|
5296
|
+
return new Error(`Install ${packageName} ${reason} (\`bun add ${packageName}\`).`, {
|
|
5297
|
+
cause: error
|
|
5298
|
+
});
|
|
5299
|
+
}
|
|
5300
|
+
|
|
5214
5301
|
// ../../src/core/database/mysqlConnection.ts
|
|
5215
|
-
|
|
5302
|
+
var MYSQL_SESSION_UTC = "SET time_zone = '+00:00'";
|
|
5303
|
+
var mysqlModule;
|
|
5304
|
+
var mysqlPending;
|
|
5305
|
+
var importMysql = defaultImportMysql;
|
|
5306
|
+
async function defaultImportMysql() {
|
|
5307
|
+
return import("mysql2/promise");
|
|
5308
|
+
}
|
|
5309
|
+
function mysqlApi(mod) {
|
|
5310
|
+
if (typeof mod.createPool === "function") {
|
|
5311
|
+
return mod;
|
|
5312
|
+
}
|
|
5313
|
+
const withDefault = mod;
|
|
5314
|
+
if (typeof withDefault.default?.createPool === "function") {
|
|
5315
|
+
return withDefault.default;
|
|
5316
|
+
}
|
|
5317
|
+
throw new Error("mysql2/promise did not export createPool.");
|
|
5318
|
+
}
|
|
5319
|
+
async function loadMysql() {
|
|
5320
|
+
if (mysqlModule) {
|
|
5321
|
+
return mysqlModule;
|
|
5322
|
+
}
|
|
5323
|
+
if (!mysqlPending) {
|
|
5324
|
+
mysqlPending = (async () => {
|
|
5325
|
+
let mod;
|
|
5326
|
+
try {
|
|
5327
|
+
mod = await importMysql();
|
|
5328
|
+
} catch (error) {
|
|
5329
|
+
mysqlPending = undefined;
|
|
5330
|
+
throw missingOptionalPeer("mysql2", "to open a MySQL connection", error);
|
|
5331
|
+
}
|
|
5332
|
+
try {
|
|
5333
|
+
mysqlModule = mysqlApi(mod);
|
|
5334
|
+
return mysqlModule;
|
|
5335
|
+
} catch (error) {
|
|
5336
|
+
mysqlPending = undefined;
|
|
5337
|
+
throw error;
|
|
5338
|
+
}
|
|
5339
|
+
})();
|
|
5340
|
+
}
|
|
5341
|
+
return mysqlPending;
|
|
5342
|
+
}
|
|
5216
5343
|
function rowsFromResult(result) {
|
|
5217
5344
|
if (Array.isArray(result)) {
|
|
5218
5345
|
return result;
|
|
@@ -5235,7 +5362,6 @@ function createMysqlConnectionFromPool(pool) {
|
|
|
5235
5362
|
}
|
|
5236
5363
|
};
|
|
5237
5364
|
}
|
|
5238
|
-
var MYSQL_SESSION_UTC = "SET time_zone = '+00:00'";
|
|
5239
5365
|
function pinSessionToUtc(connection) {
|
|
5240
5366
|
connection.query(MYSQL_SESSION_UTC, (error) => {
|
|
5241
5367
|
if (error) {
|
|
@@ -5243,18 +5369,47 @@ function pinSessionToUtc(connection) {
|
|
|
5243
5369
|
}
|
|
5244
5370
|
});
|
|
5245
5371
|
}
|
|
5246
|
-
function
|
|
5372
|
+
function createPoolFromModule(mysql, url) {
|
|
5247
5373
|
const pool = mysql.createPool({ uri: url, timezone: "Z" });
|
|
5248
5374
|
pool.on("connection", (connection) => {
|
|
5249
5375
|
pinSessionToUtc(connection);
|
|
5250
5376
|
});
|
|
5251
5377
|
return pool;
|
|
5252
5378
|
}
|
|
5379
|
+
async function createMysqlPool(url) {
|
|
5380
|
+
return createPoolFromModule(await loadMysql(), url);
|
|
5381
|
+
}
|
|
5253
5382
|
function createMysqlConnection(url) {
|
|
5254
5383
|
if (!url.trim()) {
|
|
5255
5384
|
throw new Error("MYSQL_URL is not configured. Set url before creating a MySQL pool.");
|
|
5256
5385
|
}
|
|
5257
|
-
|
|
5386
|
+
let poolPending;
|
|
5387
|
+
function ensurePool() {
|
|
5388
|
+
if (!poolPending) {
|
|
5389
|
+
poolPending = createMysqlPool(url).catch((error) => {
|
|
5390
|
+
poolPending = undefined;
|
|
5391
|
+
throw error;
|
|
5392
|
+
});
|
|
5393
|
+
}
|
|
5394
|
+
return poolPending;
|
|
5395
|
+
}
|
|
5396
|
+
return {
|
|
5397
|
+
async unsafe(query, params = []) {
|
|
5398
|
+
const [result] = await (await ensurePool()).execute(query, [...params]);
|
|
5399
|
+
return rowsFromResult(result);
|
|
5400
|
+
},
|
|
5401
|
+
async close() {
|
|
5402
|
+
if (!poolPending) {
|
|
5403
|
+
return;
|
|
5404
|
+
}
|
|
5405
|
+
const pending = poolPending;
|
|
5406
|
+
poolPending = undefined;
|
|
5407
|
+
const pool = await pending.catch(() => {
|
|
5408
|
+
return;
|
|
5409
|
+
});
|
|
5410
|
+
await pool?.end();
|
|
5411
|
+
}
|
|
5412
|
+
};
|
|
5258
5413
|
}
|
|
5259
5414
|
// ../../src/core/database/namedConnections.ts
|
|
5260
5415
|
var REGISTRY_KEY = Symbol.for("@getstrata/namedConnections");
|
|
@@ -6568,6 +6723,9 @@ function generateCspNonce() {
|
|
|
6568
6723
|
function configureContentSecurityPolicy(options) {
|
|
6569
6724
|
configuredHtmlOptions = { ...options };
|
|
6570
6725
|
}
|
|
6726
|
+
function resetContentSecurityPolicyForTests() {
|
|
6727
|
+
configuredHtmlOptions = {};
|
|
6728
|
+
}
|
|
6571
6729
|
function cloneDirectives(source) {
|
|
6572
6730
|
const copy = {};
|
|
6573
6731
|
for (const [directive, values] of Object.entries(source)) {
|
|
@@ -6678,11 +6836,6 @@ function readRequestCookie(request, name) {
|
|
|
6678
6836
|
function readBunRequestCookie(request, name) {
|
|
6679
6837
|
return request.cookies.get(name) ?? readRequestCookie(request, name);
|
|
6680
6838
|
}
|
|
6681
|
-
// ../../src/core/runtime/appEnv.ts
|
|
6682
|
-
function isProductionEnv(env = process.env) {
|
|
6683
|
-
return env.APP_ENV === "production" || env.NODE_ENV === "production";
|
|
6684
|
-
}
|
|
6685
|
-
|
|
6686
6839
|
// ../../src/core/http/corsMiddleware.ts
|
|
6687
6840
|
function defaultAllowedOrigins() {
|
|
6688
6841
|
return isProductionEnv() ? "" : "*";
|
|
@@ -6748,7 +6901,7 @@ function csrfCookieName() {
|
|
|
6748
6901
|
return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
|
|
6749
6902
|
}
|
|
6750
6903
|
function resolveCsrfSecret() {
|
|
6751
|
-
return
|
|
6904
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "csrf-secret");
|
|
6752
6905
|
}
|
|
6753
6906
|
function csrfVerifyOptions() {
|
|
6754
6907
|
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
@@ -6763,7 +6916,7 @@ function tokensMatch(left, right) {
|
|
|
6763
6916
|
}
|
|
6764
6917
|
function createCsrfTokenCookie() {
|
|
6765
6918
|
const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
|
|
6766
|
-
const secure =
|
|
6919
|
+
const secure = isProductionEnv() ? "; Secure" : "";
|
|
6767
6920
|
return {
|
|
6768
6921
|
token,
|
|
6769
6922
|
cookie: `${csrfCookieName()}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}${secure}`
|
|
@@ -6883,7 +7036,7 @@ function flashCookieName() {
|
|
|
6883
7036
|
return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
|
|
6884
7037
|
}
|
|
6885
7038
|
function resolveFlashSecret() {
|
|
6886
|
-
return
|
|
7039
|
+
return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET"], "flash-secret");
|
|
6887
7040
|
}
|
|
6888
7041
|
function signFlashPayload(payload, issuedAt) {
|
|
6889
7042
|
const signature = createHmac2("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|
|
@@ -6993,6 +7146,49 @@ function getQueryParams(request) {
|
|
|
6993
7146
|
}
|
|
6994
7147
|
return new URL(request.url).searchParams;
|
|
6995
7148
|
}
|
|
7149
|
+
function parseOptionalPositiveIntQueryParam(params, name) {
|
|
7150
|
+
const value = params.get(name);
|
|
7151
|
+
if (value === null || value.trim() === "") {
|
|
7152
|
+
return;
|
|
7153
|
+
}
|
|
7154
|
+
const parsed = Number.parseInt(value, 10);
|
|
7155
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
7156
|
+
throw new BadRequestError(`Invalid query parameter "${name}". Expected a positive integer.`);
|
|
7157
|
+
}
|
|
7158
|
+
return parsed;
|
|
7159
|
+
}
|
|
7160
|
+
function parseOptionalBooleanQueryParam(params, name) {
|
|
7161
|
+
const value = params.get(name);
|
|
7162
|
+
if (value === null || value.trim() === "") {
|
|
7163
|
+
return;
|
|
7164
|
+
}
|
|
7165
|
+
switch (value.toLowerCase()) {
|
|
7166
|
+
case "true":
|
|
7167
|
+
case "1":
|
|
7168
|
+
return true;
|
|
7169
|
+
case "false":
|
|
7170
|
+
case "0":
|
|
7171
|
+
return false;
|
|
7172
|
+
default:
|
|
7173
|
+
throw new BadRequestError(`Invalid query parameter "${name}". Expected a boolean.`);
|
|
7174
|
+
}
|
|
7175
|
+
}
|
|
7176
|
+
function parseOptionalEnumQueryParam(params, name, allowedValues) {
|
|
7177
|
+
const value = params.get(name);
|
|
7178
|
+
if (value === null || value.trim() === "") {
|
|
7179
|
+
return;
|
|
7180
|
+
}
|
|
7181
|
+
if (!allowedValues.includes(value)) {
|
|
7182
|
+
throw new BadRequestError(`Invalid query parameter "${name}". Expected one of: ${allowedValues.join(", ")}.`);
|
|
7183
|
+
}
|
|
7184
|
+
return value;
|
|
7185
|
+
}
|
|
7186
|
+
function expectObject(value, label = "request body") {
|
|
7187
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
7188
|
+
throw new BadRequestError(`${label} must be a JSON object.`);
|
|
7189
|
+
}
|
|
7190
|
+
return value;
|
|
7191
|
+
}
|
|
6996
7192
|
async function parseJsonBody(request, validator) {
|
|
6997
7193
|
let payload;
|
|
6998
7194
|
try {
|
|
@@ -7002,6 +7198,55 @@ async function parseJsonBody(request, validator) {
|
|
|
7002
7198
|
}
|
|
7003
7199
|
return validator(payload);
|
|
7004
7200
|
}
|
|
7201
|
+
function readRequiredString(payload, field, options = {}) {
|
|
7202
|
+
const value = payload[field];
|
|
7203
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
7204
|
+
throw new BadRequestError(`"${field}" is required and must be a string.`);
|
|
7205
|
+
}
|
|
7206
|
+
const trimmed = value.trim();
|
|
7207
|
+
if (options.minLength !== undefined && trimmed.length < options.minLength) {
|
|
7208
|
+
throw new BadRequestError(`"${field}" must be at least ${options.minLength} characters.`);
|
|
7209
|
+
}
|
|
7210
|
+
if (options.maxLength !== undefined && trimmed.length > options.maxLength) {
|
|
7211
|
+
throw new BadRequestError(`"${field}" must be at most ${options.maxLength} characters.`);
|
|
7212
|
+
}
|
|
7213
|
+
if (options.pattern && !options.pattern.test(trimmed)) {
|
|
7214
|
+
throw new BadRequestError(`"${field}" has an invalid format.`);
|
|
7215
|
+
}
|
|
7216
|
+
return trimmed;
|
|
7217
|
+
}
|
|
7218
|
+
function readOptionalString(payload, field, options = {}) {
|
|
7219
|
+
if (!(field in payload) || payload[field] === undefined) {
|
|
7220
|
+
return;
|
|
7221
|
+
}
|
|
7222
|
+
return readRequiredString(payload, field, options);
|
|
7223
|
+
}
|
|
7224
|
+
function readRequiredEnum(payload, field, allowedValues) {
|
|
7225
|
+
const value = readRequiredString(payload, field);
|
|
7226
|
+
if (!allowedValues.includes(value)) {
|
|
7227
|
+
throw new BadRequestError(`"${field}" must be one of: ${allowedValues.join(", ")}.`);
|
|
7228
|
+
}
|
|
7229
|
+
return value;
|
|
7230
|
+
}
|
|
7231
|
+
function readOptionalEnum(payload, field, allowedValues) {
|
|
7232
|
+
if (!(field in payload) || payload[field] === undefined) {
|
|
7233
|
+
return;
|
|
7234
|
+
}
|
|
7235
|
+
return readRequiredEnum(payload, field, allowedValues);
|
|
7236
|
+
}
|
|
7237
|
+
function readRequiredPositiveInt(payload, field) {
|
|
7238
|
+
const value = payload[field];
|
|
7239
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
7240
|
+
throw new BadRequestError(`"${field}" is required and must be a positive integer.`);
|
|
7241
|
+
}
|
|
7242
|
+
return value;
|
|
7243
|
+
}
|
|
7244
|
+
function readOptionalPositiveInt(payload, field) {
|
|
7245
|
+
if (!(field in payload) || payload[field] === undefined) {
|
|
7246
|
+
return;
|
|
7247
|
+
}
|
|
7248
|
+
return readRequiredPositiveInt(payload, field);
|
|
7249
|
+
}
|
|
7005
7250
|
function parsePositiveIntParam(value, name = "id") {
|
|
7006
7251
|
const parsed = Number.parseInt(value, 10);
|
|
7007
7252
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
@@ -7022,6 +7267,12 @@ class FormRequest {
|
|
|
7022
7267
|
return await parseJsonBody(request, (payload) => this.parse(payload));
|
|
7023
7268
|
}
|
|
7024
7269
|
}
|
|
7270
|
+
|
|
7271
|
+
class QueryFormRequest {
|
|
7272
|
+
validate(request) {
|
|
7273
|
+
return this.parseQuery(request);
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7025
7276
|
// ../../src/core/http/authMiddleware.ts
|
|
7026
7277
|
function createAuthMiddleware(auth) {
|
|
7027
7278
|
return async (request, next) => {
|
|
@@ -7584,6 +7835,10 @@ function withErrorHandling(handler) {
|
|
|
7584
7835
|
}
|
|
7585
7836
|
};
|
|
7586
7837
|
}
|
|
7838
|
+
// ../../src/core/http/route.ts
|
|
7839
|
+
function getRouteParams(request) {
|
|
7840
|
+
return request.params;
|
|
7841
|
+
}
|
|
7587
7842
|
// ../../src/core/http/routeMiddleware.ts
|
|
7588
7843
|
function withMiddleware(...middleware) {
|
|
7589
7844
|
const wrap = composeMiddleware(...middleware);
|
|
@@ -7757,6 +8012,9 @@ function createLoginThrottleMiddleware(options) {
|
|
|
7757
8012
|
}
|
|
7758
8013
|
return createMemoryLoginThrottleMiddleware(options);
|
|
7759
8014
|
}
|
|
8015
|
+
function resetMemoryLoginThrottleForTests() {
|
|
8016
|
+
memoryLoginBuckets.clear();
|
|
8017
|
+
}
|
|
7760
8018
|
// ../../src/core/http/memoryThrottleMiddleware.ts
|
|
7761
8019
|
var throttleBucketRegistries = new Set;
|
|
7762
8020
|
function createMemoryThrottleMiddleware(options) {
|
|
@@ -7944,7 +8202,7 @@ function intendedUrlTtlSeconds() {
|
|
|
7944
8202
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_INTENDED_URL_TTL_SECONDS;
|
|
7945
8203
|
}
|
|
7946
8204
|
function cookieSecureFlag() {
|
|
7947
|
-
return
|
|
8205
|
+
return isProductionEnv() ? "; Secure" : "";
|
|
7948
8206
|
}
|
|
7949
8207
|
function pathnameOf(path) {
|
|
7950
8208
|
const pathname = path.split("?")[0] ?? path;
|
|
@@ -8065,7 +8323,7 @@ function createSecurityHeadersMiddleware(options = {}) {
|
|
|
8065
8323
|
// ../../src/core/http/signedUrl.ts
|
|
8066
8324
|
import { createHmac as createHmac3 } from "crypto";
|
|
8067
8325
|
function resolveSignedUrlSecret() {
|
|
8068
|
-
return
|
|
8326
|
+
return requireConfiguredSecret(["SIGNED_URL_SECRET", "SESSION_SECRET", "OAUTH_STATE_SECRET"], "signed-url-secret");
|
|
8069
8327
|
}
|
|
8070
8328
|
function resolveSignedUrlOrigin() {
|
|
8071
8329
|
return (process.env.APP_URL ?? "http://localhost:3000").replace(/\/$/, "");
|
|
@@ -8264,6 +8522,11 @@ function installGracefulShutdownSignals(signals = ["SIGINT", "SIGTERM"]) {
|
|
|
8264
8522
|
});
|
|
8265
8523
|
}
|
|
8266
8524
|
}
|
|
8525
|
+
function resetGracefulShutdownForTests() {
|
|
8526
|
+
shutdownHandlers.clear();
|
|
8527
|
+
shutdownInstalled = false;
|
|
8528
|
+
shuttingDown = false;
|
|
8529
|
+
}
|
|
8267
8530
|
// ../../src/core/logging/requestLoggingMiddleware.ts
|
|
8268
8531
|
function createRequestLoggingMiddleware() {
|
|
8269
8532
|
return async (request, next) => {
|
|
@@ -8509,6 +8772,42 @@ class AsyncQueue {
|
|
|
8509
8772
|
function createQueue(driver) {
|
|
8510
8773
|
return driver === "async" ? new AsyncQueue : new SyncQueue;
|
|
8511
8774
|
}
|
|
8775
|
+
// ../../src/core/queue/jobRegistry.ts
|
|
8776
|
+
class JobRegistry {
|
|
8777
|
+
factories = new Map;
|
|
8778
|
+
instances = new WeakMap;
|
|
8779
|
+
register(name, factory) {
|
|
8780
|
+
this.factories.set(name, factory);
|
|
8781
|
+
}
|
|
8782
|
+
resolveName(job) {
|
|
8783
|
+
return this.instances.get(job);
|
|
8784
|
+
}
|
|
8785
|
+
track(name, job) {
|
|
8786
|
+
this.instances.set(job, name);
|
|
8787
|
+
return job;
|
|
8788
|
+
}
|
|
8789
|
+
create(name) {
|
|
8790
|
+
const factory = this.factories.get(name);
|
|
8791
|
+
if (!factory) {
|
|
8792
|
+
return;
|
|
8793
|
+
}
|
|
8794
|
+
return factory();
|
|
8795
|
+
}
|
|
8796
|
+
names() {
|
|
8797
|
+
return [...this.factories.keys()];
|
|
8798
|
+
}
|
|
8799
|
+
}
|
|
8800
|
+
var JOB_REGISTRY_KEY = Symbol.for("@getstrata/jobRegistry");
|
|
8801
|
+
function readSharedJobRegistry() {
|
|
8802
|
+
const globalRegistry = globalThis[JOB_REGISTRY_KEY];
|
|
8803
|
+
if (globalRegistry) {
|
|
8804
|
+
return globalRegistry;
|
|
8805
|
+
}
|
|
8806
|
+
const registry = new JobRegistry;
|
|
8807
|
+
globalThis[JOB_REGISTRY_KEY] = registry;
|
|
8808
|
+
return registry;
|
|
8809
|
+
}
|
|
8810
|
+
var jobRegistry = readSharedJobRegistry();
|
|
8512
8811
|
// ../../src/core/queue/failedJobTable.ts
|
|
8513
8812
|
var failedJobTable = defineTable({
|
|
8514
8813
|
name: "failed_job",
|
|
@@ -8569,43 +8868,6 @@ class FailedJobService {
|
|
|
8569
8868
|
}
|
|
8570
8869
|
var failedJobService_default = FailedJobService;
|
|
8571
8870
|
|
|
8572
|
-
// ../../src/core/queue/jobRegistry.ts
|
|
8573
|
-
class JobRegistry {
|
|
8574
|
-
factories = new Map;
|
|
8575
|
-
instances = new WeakMap;
|
|
8576
|
-
register(name, factory) {
|
|
8577
|
-
this.factories.set(name, factory);
|
|
8578
|
-
}
|
|
8579
|
-
resolveName(job) {
|
|
8580
|
-
return this.instances.get(job);
|
|
8581
|
-
}
|
|
8582
|
-
track(name, job) {
|
|
8583
|
-
this.instances.set(job, name);
|
|
8584
|
-
return job;
|
|
8585
|
-
}
|
|
8586
|
-
create(name) {
|
|
8587
|
-
const factory = this.factories.get(name);
|
|
8588
|
-
if (!factory) {
|
|
8589
|
-
return;
|
|
8590
|
-
}
|
|
8591
|
-
return factory();
|
|
8592
|
-
}
|
|
8593
|
-
names() {
|
|
8594
|
-
return [...this.factories.keys()];
|
|
8595
|
-
}
|
|
8596
|
-
}
|
|
8597
|
-
var JOB_REGISTRY_KEY = Symbol.for("@getstrata/jobRegistry");
|
|
8598
|
-
function readSharedJobRegistry() {
|
|
8599
|
-
const globalRegistry = globalThis[JOB_REGISTRY_KEY];
|
|
8600
|
-
if (globalRegistry) {
|
|
8601
|
-
return globalRegistry;
|
|
8602
|
-
}
|
|
8603
|
-
const registry = new JobRegistry;
|
|
8604
|
-
globalThis[JOB_REGISTRY_KEY] = registry;
|
|
8605
|
-
return registry;
|
|
8606
|
-
}
|
|
8607
|
-
var jobRegistry = readSharedJobRegistry();
|
|
8608
|
-
|
|
8609
8871
|
// ../../src/core/queue/queueConfig.ts
|
|
8610
8872
|
function resolveQueueConfig() {
|
|
8611
8873
|
const driver = process.env.QUEUE_DRIVER;
|
|
@@ -8952,20 +9214,131 @@ function isPublicReadsEnabled() {
|
|
|
8952
9214
|
function guestCanViewResource() {
|
|
8953
9215
|
return isPublicReadsEnabled();
|
|
8954
9216
|
}
|
|
9217
|
+
// ../../src/core/security/safeUrl.ts
|
|
9218
|
+
import { lookup as dnsLookupImpl } from "dns/promises";
|
|
9219
|
+
var dnsLookup = dnsLookupImpl;
|
|
9220
|
+
var BLOCKED_HOSTNAMES = new Set([
|
|
9221
|
+
"localhost",
|
|
9222
|
+
"127.0.0.1",
|
|
9223
|
+
"0.0.0.0",
|
|
9224
|
+
"::1",
|
|
9225
|
+
"metadata.google.internal"
|
|
9226
|
+
]);
|
|
9227
|
+
function isPrivateIpv4(hostname) {
|
|
9228
|
+
const match = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(hostname);
|
|
9229
|
+
if (!match) {
|
|
9230
|
+
return false;
|
|
9231
|
+
}
|
|
9232
|
+
const octets = match.slice(1, 5).map((part) => Number.parseInt(part, 10));
|
|
9233
|
+
if (octets.some((octet) => octet < 0 || octet > 255)) {
|
|
9234
|
+
return true;
|
|
9235
|
+
}
|
|
9236
|
+
const [a = 0, b = 0] = octets;
|
|
9237
|
+
if (a === 10) {
|
|
9238
|
+
return true;
|
|
9239
|
+
}
|
|
9240
|
+
if (a === 127) {
|
|
9241
|
+
return true;
|
|
9242
|
+
}
|
|
9243
|
+
if (a === 0) {
|
|
9244
|
+
return true;
|
|
9245
|
+
}
|
|
9246
|
+
if (a === 169 && b === 254) {
|
|
9247
|
+
return true;
|
|
9248
|
+
}
|
|
9249
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
9250
|
+
return true;
|
|
9251
|
+
}
|
|
9252
|
+
if (a === 192 && b === 168) {
|
|
9253
|
+
return true;
|
|
9254
|
+
}
|
|
9255
|
+
return false;
|
|
9256
|
+
}
|
|
9257
|
+
function isBlockedHostname(hostname) {
|
|
9258
|
+
const normalized = hostname.trim().toLowerCase();
|
|
9259
|
+
if (normalized.length === 0) {
|
|
9260
|
+
return true;
|
|
9261
|
+
}
|
|
9262
|
+
if (BLOCKED_HOSTNAMES.has(normalized)) {
|
|
9263
|
+
return true;
|
|
9264
|
+
}
|
|
9265
|
+
if (normalized.endsWith(".local") || normalized.endsWith(".internal")) {
|
|
9266
|
+
return true;
|
|
9267
|
+
}
|
|
9268
|
+
if (normalized.includes(":")) {
|
|
9269
|
+
return true;
|
|
9270
|
+
}
|
|
9271
|
+
return isPrivateIpv4(normalized);
|
|
9272
|
+
}
|
|
9273
|
+
function assertSafeOutboundUrl(rawUrl, options = {}) {
|
|
9274
|
+
let parsed;
|
|
9275
|
+
try {
|
|
9276
|
+
parsed = new URL(rawUrl);
|
|
9277
|
+
} catch {
|
|
9278
|
+
throw new BadRequestError("Webhook URL is invalid.");
|
|
9279
|
+
}
|
|
9280
|
+
if (parsed.protocol !== "https:" && !(options.allowHttp && parsed.protocol === "http:")) {
|
|
9281
|
+
throw new BadRequestError("Webhook URL must use HTTPS.");
|
|
9282
|
+
}
|
|
9283
|
+
if (parsed.username || parsed.password) {
|
|
9284
|
+
throw new BadRequestError("Webhook URL must not include credentials.");
|
|
9285
|
+
}
|
|
9286
|
+
if (isBlockedHostname(parsed.hostname)) {
|
|
9287
|
+
throw new BadRequestError("Webhook URL targets a blocked host.");
|
|
9288
|
+
}
|
|
9289
|
+
return parsed;
|
|
9290
|
+
}
|
|
9291
|
+
function isBlockedIpAddress(address) {
|
|
9292
|
+
return isBlockedHostname(address.trim().toLowerCase());
|
|
9293
|
+
}
|
|
9294
|
+
async function assertSafeOutboundUrlResolved(rawUrl, options = {}) {
|
|
9295
|
+
const parsed = assertSafeOutboundUrl(rawUrl, options);
|
|
9296
|
+
if (options.resolveDns === false) {
|
|
9297
|
+
return parsed;
|
|
9298
|
+
}
|
|
9299
|
+
const hostname = parsed.hostname.trim().toLowerCase();
|
|
9300
|
+
const results = await dnsLookup(hostname, { all: true, verbatim: true });
|
|
9301
|
+
if (results.some((result) => isBlockedIpAddress(result.address))) {
|
|
9302
|
+
throw new BadRequestError("Webhook URL targets a blocked host.");
|
|
9303
|
+
}
|
|
9304
|
+
return parsed;
|
|
9305
|
+
}
|
|
9306
|
+
function setDnsLookupForTests(lookupFn) {
|
|
9307
|
+
dnsLookup = lookupFn;
|
|
9308
|
+
}
|
|
9309
|
+
function resetDnsLookupForTests() {
|
|
9310
|
+
dnsLookup = dnsLookupImpl;
|
|
9311
|
+
}
|
|
8955
9312
|
// ../../src/core/tenant/tenantMiddleware.ts
|
|
8956
9313
|
import { createHash } from "crypto";
|
|
8957
9314
|
|
|
8958
9315
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
9316
|
+
async function applyBypassToTransaction(transaction, bypass) {
|
|
9317
|
+
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
9318
|
+
bypass ? "true" : "false"
|
|
9319
|
+
]);
|
|
9320
|
+
}
|
|
8959
9321
|
async function runWithMigrationBypass(callback) {
|
|
8960
9322
|
if (!isRlsTenancy()) {
|
|
8961
9323
|
return await callback();
|
|
8962
9324
|
}
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
9325
|
+
if (hasActiveDatabaseConnection()) {
|
|
9326
|
+
const activeConnection = getActiveDatabaseConnection(getDefaultDatabasePool());
|
|
9327
|
+
await applyBypassToTransaction(activeConnection, true);
|
|
9328
|
+
try {
|
|
9329
|
+
return await callback();
|
|
9330
|
+
} finally {
|
|
9331
|
+
await applyBypassToTransaction(activeConnection, false);
|
|
9332
|
+
}
|
|
8968
9333
|
}
|
|
9334
|
+
const pool = getDefaultDatabasePool();
|
|
9335
|
+
if (typeof pool.begin !== "function") {
|
|
9336
|
+
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
9337
|
+
}
|
|
9338
|
+
return await pool.begin(async (transaction) => {
|
|
9339
|
+
await applyBypassToTransaction(transaction, true);
|
|
9340
|
+
return await runWithDatabaseConnection(transaction, callback);
|
|
9341
|
+
});
|
|
8969
9342
|
}
|
|
8970
9343
|
|
|
8971
9344
|
// ../../src/core/tenant/tenantMiddleware.ts
|
|
@@ -9229,7 +9602,6 @@ function validateObject(payload, schema) {
|
|
|
9229
9602
|
}
|
|
9230
9603
|
// ../../src/core/view/etaViewEngine.ts
|
|
9231
9604
|
import { join as join4, relative } from "path";
|
|
9232
|
-
import { Eta } from "eta";
|
|
9233
9605
|
|
|
9234
9606
|
// ../../src/core/view/assertEtaHtmlSource.ts
|
|
9235
9607
|
var HTML_TAGS = new Set([
|
|
@@ -9409,33 +9781,57 @@ var DEFAULT_VIEWS_DIRECTORY = join4(process.cwd(), "resources/views");
|
|
|
9409
9781
|
var DEFAULT_LAYOUT = "layouts/app.eta";
|
|
9410
9782
|
|
|
9411
9783
|
class EtaViewEngine {
|
|
9412
|
-
eta;
|
|
9784
|
+
eta = null;
|
|
9785
|
+
etaPending = null;
|
|
9786
|
+
viewsDirectory;
|
|
9413
9787
|
resolveLayoutData;
|
|
9414
9788
|
constructor(viewsDirectory = DEFAULT_VIEWS_DIRECTORY, resolveLayoutData) {
|
|
9415
|
-
this.
|
|
9416
|
-
views: viewsDirectory,
|
|
9417
|
-
autoTrim: false
|
|
9418
|
-
});
|
|
9789
|
+
this.viewsDirectory = viewsDirectory;
|
|
9419
9790
|
this.resolveLayoutData = resolveLayoutData;
|
|
9420
|
-
|
|
9421
|
-
|
|
9791
|
+
}
|
|
9792
|
+
async getEta() {
|
|
9793
|
+
if (this.eta) {
|
|
9794
|
+
return this.eta;
|
|
9795
|
+
}
|
|
9796
|
+
if (!this.etaPending) {
|
|
9797
|
+
this.etaPending = this.createEta();
|
|
9798
|
+
}
|
|
9799
|
+
return this.etaPending;
|
|
9800
|
+
}
|
|
9801
|
+
async createEta() {
|
|
9802
|
+
let eta;
|
|
9803
|
+
try {
|
|
9804
|
+
const mod = await import("eta");
|
|
9805
|
+
eta = new mod.Eta({
|
|
9806
|
+
views: this.viewsDirectory,
|
|
9807
|
+
autoTrim: false
|
|
9808
|
+
});
|
|
9809
|
+
} catch (error) {
|
|
9810
|
+
this.etaPending = null;
|
|
9811
|
+
throw missingOptionalPeer("eta", "to render HTML views", error);
|
|
9812
|
+
}
|
|
9813
|
+
const readFile = eta.readFile?.bind(eta);
|
|
9814
|
+
eta.readFile = (path) => {
|
|
9422
9815
|
const source = readFile ? readFile(path) : "";
|
|
9423
|
-
assertEtaHtmlSource(relative(viewsDirectory, path) || path, source);
|
|
9816
|
+
assertEtaHtmlSource(relative(this.viewsDirectory, path) || path, source);
|
|
9424
9817
|
return source;
|
|
9425
9818
|
};
|
|
9819
|
+
this.eta = eta;
|
|
9820
|
+
return eta;
|
|
9426
9821
|
}
|
|
9427
9822
|
async render(name, data = {}, options = {}) {
|
|
9428
9823
|
const template = name.endsWith(".eta") ? name : `${name}.eta`;
|
|
9429
9824
|
const request = options.request ?? currentRequestMeta().request;
|
|
9430
9825
|
const layoutData = this.resolveLayoutData ? await this.resolveLayoutData(request) : {};
|
|
9431
9826
|
const mergedData = { ...layoutData, ...data };
|
|
9432
|
-
const
|
|
9827
|
+
const eta = await this.getEta();
|
|
9828
|
+
const body = await eta.renderAsync(template, mergedData);
|
|
9433
9829
|
const layout = options.layout ?? DEFAULT_LAYOUT;
|
|
9434
9830
|
if (layout === false) {
|
|
9435
9831
|
return body;
|
|
9436
9832
|
}
|
|
9437
9833
|
const layoutTemplate = layout.endsWith(".eta") ? layout : `${layout}.eta`;
|
|
9438
|
-
return await
|
|
9834
|
+
return await eta.renderAsync(layoutTemplate, {
|
|
9439
9835
|
...mergedData,
|
|
9440
9836
|
body
|
|
9441
9837
|
});
|
|
@@ -9510,6 +9906,7 @@ export {
|
|
|
9510
9906
|
CompositeGuard,
|
|
9511
9907
|
ConfigStore,
|
|
9512
9908
|
ConflictError,
|
|
9909
|
+
DEFAULT_PER_PAGE,
|
|
9513
9910
|
DEFAULT_TENANT,
|
|
9514
9911
|
DEFAULT_VIEWS_DIRECTORY,
|
|
9515
9912
|
DatabaseTokenGuard,
|
|
@@ -9522,15 +9919,19 @@ export {
|
|
|
9522
9919
|
ForeignIdColumnDefinition,
|
|
9523
9920
|
FormRequest,
|
|
9524
9921
|
GuestGuard,
|
|
9922
|
+
HTMX_2_0_4_INDICATOR_STYLE_HASH,
|
|
9525
9923
|
HasManyRelationQuery,
|
|
9924
|
+
HasManyThroughRelationQuery,
|
|
9526
9925
|
HasOneRelationQuery,
|
|
9527
9926
|
HttpError,
|
|
9528
9927
|
InternalServerError,
|
|
9529
9928
|
Job,
|
|
9929
|
+
JobRegistry,
|
|
9530
9930
|
JsonResource,
|
|
9531
9931
|
JwtGuard,
|
|
9532
9932
|
LocalStorageDriver,
|
|
9533
9933
|
LogMailDriver,
|
|
9934
|
+
MAX_PER_PAGE,
|
|
9534
9935
|
Mailer,
|
|
9535
9936
|
membershipService_default as MembershipService,
|
|
9536
9937
|
Model,
|
|
@@ -9548,6 +9949,7 @@ export {
|
|
|
9548
9949
|
PostgresGrammar,
|
|
9549
9950
|
PreconditionFailedError,
|
|
9550
9951
|
PrometheusRegistry,
|
|
9952
|
+
QueryFormRequest,
|
|
9551
9953
|
QueueWorker,
|
|
9552
9954
|
RedisQueue,
|
|
9553
9955
|
RepositoryQuery,
|
|
@@ -9566,7 +9968,15 @@ export {
|
|
|
9566
9968
|
WebFormRequest,
|
|
9567
9969
|
WhereBuilder,
|
|
9568
9970
|
absoluteTemporarySignedUrl,
|
|
9971
|
+
apiPrefix,
|
|
9972
|
+
appCookieName,
|
|
9973
|
+
appDevSecret,
|
|
9974
|
+
appDisplayName,
|
|
9975
|
+
appEnv,
|
|
9976
|
+
appKeyPrefix,
|
|
9569
9977
|
appSchedule,
|
|
9978
|
+
appUrl,
|
|
9979
|
+
appUserAgent,
|
|
9570
9980
|
appendOrganizationScope,
|
|
9571
9981
|
appendProjectScope,
|
|
9572
9982
|
applyCasts,
|
|
@@ -9575,6 +9985,8 @@ export {
|
|
|
9575
9985
|
assertIfMatch,
|
|
9576
9986
|
assertOrganizationReadable,
|
|
9577
9987
|
assertResourceInCurrentTenant,
|
|
9988
|
+
assertSafeOutboundUrl,
|
|
9989
|
+
assertSafeOutboundUrlResolved,
|
|
9578
9990
|
assertValidSignature,
|
|
9579
9991
|
auditChecksum,
|
|
9580
9992
|
auth,
|
|
@@ -9584,13 +9996,29 @@ export {
|
|
|
9584
9996
|
bindBunSql,
|
|
9585
9997
|
bindDatabaseConnection,
|
|
9586
9998
|
bindRouteModel,
|
|
9999
|
+
buildAdvancedWhereClause,
|
|
10000
|
+
buildCountQuery,
|
|
10001
|
+
buildDeleteByIdQuery,
|
|
10002
|
+
buildGroupedCountQuery,
|
|
10003
|
+
buildInsertQuery,
|
|
10004
|
+
buildJoinClause,
|
|
9587
10005
|
buildMarkdownMailMessage,
|
|
10006
|
+
buildOrderByClause,
|
|
10007
|
+
buildPaginationMeta,
|
|
10008
|
+
buildProjectionQuery,
|
|
10009
|
+
buildQueryWhereClause,
|
|
9588
10010
|
buildRequestCacheKey,
|
|
10011
|
+
buildRestoreByIdQuery,
|
|
10012
|
+
buildSelectQuery,
|
|
9589
10013
|
buildSmtpPayload,
|
|
10014
|
+
buildSoftDeleteByIdQuery,
|
|
10015
|
+
buildUpdateQuery,
|
|
10016
|
+
buildWhereClause,
|
|
9590
10017
|
cache,
|
|
9591
10018
|
collectQueueMetrics,
|
|
9592
10019
|
compileBlueprint,
|
|
9593
10020
|
composeMiddleware,
|
|
10021
|
+
computeEtagFromJson,
|
|
9594
10022
|
conditionalJsonResponse,
|
|
9595
10023
|
config,
|
|
9596
10024
|
configureContentSecurityPolicy,
|
|
@@ -9612,6 +10040,7 @@ export {
|
|
|
9612
10040
|
createFlashMiddleware,
|
|
9613
10041
|
createLoginThrottleMiddleware,
|
|
9614
10042
|
createMembershipMiddleware,
|
|
10043
|
+
createMemoryLoginThrottleMiddleware,
|
|
9615
10044
|
createMemoryThrottleMiddleware,
|
|
9616
10045
|
createMetricsMiddleware,
|
|
9617
10046
|
createMysqlConnection,
|
|
@@ -9652,10 +10081,14 @@ export {
|
|
|
9652
10081
|
dialectFor,
|
|
9653
10082
|
emailRule,
|
|
9654
10083
|
emptyPaginateResult,
|
|
10084
|
+
envFlagEnabled,
|
|
10085
|
+
errorResponse,
|
|
9655
10086
|
errorTemplateName,
|
|
9656
10087
|
etagFromResource,
|
|
10088
|
+
etagValuesMatch,
|
|
9657
10089
|
eventBus,
|
|
9658
10090
|
events,
|
|
10091
|
+
expectObject,
|
|
9659
10092
|
filterMassAssignable,
|
|
9660
10093
|
foreignKeyFromTable,
|
|
9661
10094
|
formatAdminValue,
|
|
@@ -9667,10 +10100,13 @@ export {
|
|
|
9667
10100
|
getDefaultDatabaseQuery,
|
|
9668
10101
|
getMigrationStatus,
|
|
9669
10102
|
getNamedConnection,
|
|
10103
|
+
getQueryParams,
|
|
10104
|
+
getRouteParams,
|
|
9670
10105
|
grammarForDriver,
|
|
9671
10106
|
guestCanViewResource,
|
|
9672
10107
|
hasActiveDatabaseConnection,
|
|
9673
10108
|
hasMany,
|
|
10109
|
+
hasManyThrough,
|
|
9674
10110
|
hasMinimumOrgRole2 as hasMinimumOrgRole,
|
|
9675
10111
|
hasNamedConnection,
|
|
9676
10112
|
hasOne,
|
|
@@ -9679,20 +10115,26 @@ export {
|
|
|
9679
10115
|
htmlErrorResponse,
|
|
9680
10116
|
htmlResponse,
|
|
9681
10117
|
hydrateValue,
|
|
10118
|
+
ifMatchSatisfied,
|
|
10119
|
+
ifNoneMatchSatisfied,
|
|
9682
10120
|
indexBelongsToManyRelation,
|
|
9683
10121
|
indexBelongsToRelation,
|
|
9684
10122
|
indexHasManyRelation,
|
|
10123
|
+
indexHasManyThroughRelation,
|
|
9685
10124
|
indexHasOneRelation,
|
|
9686
10125
|
indexMorphManyRelation,
|
|
9687
10126
|
indexMorphOneRelation,
|
|
9688
10127
|
indexMorphToRelation,
|
|
9689
10128
|
inferReferencedTable,
|
|
9690
10129
|
installGracefulShutdownSignals,
|
|
10130
|
+
isBlockedHostname,
|
|
10131
|
+
isBlockedIpAddress,
|
|
9691
10132
|
isEtagEnabled,
|
|
9692
10133
|
isGlobalAdmin,
|
|
9693
10134
|
isHtmxRequest,
|
|
9694
10135
|
isHttpErrorLike,
|
|
9695
10136
|
isInsideTenantDatabaseScope,
|
|
10137
|
+
isProductionEnv,
|
|
9696
10138
|
isPublicReadsEnabled,
|
|
9697
10139
|
isRlsTenancy,
|
|
9698
10140
|
isTenancyEnabled,
|
|
@@ -9703,32 +10145,54 @@ export {
|
|
|
9703
10145
|
loadSeedersFromDirectory,
|
|
9704
10146
|
log,
|
|
9705
10147
|
logSecurityEvent,
|
|
10148
|
+
logServerError,
|
|
9706
10149
|
loginRedirectLocation,
|
|
9707
10150
|
mail,
|
|
9708
10151
|
mailer,
|
|
10152
|
+
mapDatabaseError,
|
|
9709
10153
|
markdownToHtml,
|
|
9710
10154
|
maxLength,
|
|
10155
|
+
membershipContext,
|
|
10156
|
+
membershipRepository,
|
|
9711
10157
|
migrateDatabase,
|
|
9712
10158
|
minLength,
|
|
9713
10159
|
modelEventName,
|
|
9714
10160
|
morphMany,
|
|
9715
10161
|
morphOne,
|
|
9716
10162
|
morphTo,
|
|
10163
|
+
namespacedRedisKey,
|
|
9717
10164
|
noContentResponse,
|
|
10165
|
+
normalizeFieldErrors,
|
|
9718
10166
|
normalizeMetricPath,
|
|
9719
10167
|
notFoundHtmlResponse,
|
|
10168
|
+
notModifiedResponse,
|
|
10169
|
+
otelServiceName,
|
|
9720
10170
|
paginatedResponse,
|
|
10171
|
+
parseJsonBody,
|
|
9721
10172
|
parseMultipartUpload,
|
|
10173
|
+
parseOptionalBooleanQueryParam,
|
|
10174
|
+
parseOptionalEnumQueryParam,
|
|
10175
|
+
parseOptionalPositiveIntQueryParam,
|
|
9722
10176
|
parsePaginationQuery,
|
|
9723
10177
|
parsePositiveIntParam,
|
|
10178
|
+
parseQualifiedColumn,
|
|
9724
10179
|
pivotTableName,
|
|
9725
10180
|
policyGate,
|
|
9726
10181
|
prometheusRegistry,
|
|
10182
|
+
qualifyColumn,
|
|
9727
10183
|
queue,
|
|
10184
|
+
quoteIdentifier,
|
|
9728
10185
|
rateLimitMultiplierForPlan,
|
|
9729
10186
|
readBunRequestCookie,
|
|
9730
10187
|
readClientIp,
|
|
10188
|
+
readOptionalEnum,
|
|
10189
|
+
readOptionalPositiveInt,
|
|
10190
|
+
readOptionalString,
|
|
9731
10191
|
readRequestCookie,
|
|
10192
|
+
readRequiredEnum,
|
|
10193
|
+
readRequiredPositiveInt,
|
|
10194
|
+
readRequiredString,
|
|
10195
|
+
readSharedEventBus,
|
|
9732
10196
|
readSubmittedCsrfToken,
|
|
9733
10197
|
readSubmittedCsrfTokenFromBody,
|
|
9734
10198
|
readTenancyDriver,
|
|
@@ -9740,11 +10204,20 @@ export {
|
|
|
9740
10204
|
registerShutdownHandler,
|
|
9741
10205
|
renderKernelErrorChrome,
|
|
9742
10206
|
renderMarkdownMail,
|
|
10207
|
+
renderWebErrorHtml,
|
|
9743
10208
|
repositoryConnection,
|
|
9744
10209
|
requestIdMiddleware,
|
|
10210
|
+
requestMetaContext,
|
|
10211
|
+
requireConfiguredSecret,
|
|
9745
10212
|
required,
|
|
9746
10213
|
resetBoundDatabaseConnection,
|
|
10214
|
+
resetContentSecurityPolicyForTests,
|
|
10215
|
+
resetDefaultDatabasePoolForTests,
|
|
9747
10216
|
resetDefaultStorage,
|
|
10217
|
+
resetDnsLookupForTests,
|
|
10218
|
+
resetGracefulShutdownForTests,
|
|
10219
|
+
resetMembershipLookupForTests,
|
|
10220
|
+
resetMemoryLoginThrottleForTests,
|
|
9748
10221
|
resetMemoryThrottleForTests,
|
|
9749
10222
|
resetNamedConnections,
|
|
9750
10223
|
resetSqlDialect,
|
|
@@ -9761,11 +10234,15 @@ export {
|
|
|
9761
10234
|
resolveCsrfTokenForRequest,
|
|
9762
10235
|
resolveDatabaseDriver,
|
|
9763
10236
|
resolveHtmlContentSecurityPolicy,
|
|
10237
|
+
resolveLoginEmail,
|
|
10238
|
+
resolveLoginIdentity,
|
|
9764
10239
|
resolveMembershipLookup,
|
|
9765
10240
|
resolveMembershipService,
|
|
9766
10241
|
resolveOrganizationScope,
|
|
10242
|
+
resolveQualifiedColumn,
|
|
9767
10243
|
resolveRepositoryConnection,
|
|
9768
10244
|
resolveService,
|
|
10245
|
+
resolveSoftDeleteColumn,
|
|
9769
10246
|
resolveUserId,
|
|
9770
10247
|
resolveUserTenantId,
|
|
9771
10248
|
resolveWebLayoutData,
|
|
@@ -9788,16 +10265,21 @@ export {
|
|
|
9788
10265
|
safeInternalRedirectPath,
|
|
9789
10266
|
sanitizeInternalPath,
|
|
9790
10267
|
sanitizeMailHtml,
|
|
10268
|
+
sanitizeUploadFileName,
|
|
9791
10269
|
scopedOrganizationIds,
|
|
10270
|
+
sdkClientClassName,
|
|
9792
10271
|
securedBindRouteModel,
|
|
9793
10272
|
securedBindRouteModelByKey,
|
|
9794
10273
|
sendMarkdownMail,
|
|
9795
10274
|
serializeDate,
|
|
9796
10275
|
serverHtmxContentSecurityPolicy,
|
|
9797
10276
|
setActiveApplicationContext,
|
|
10277
|
+
setDnsLookupForTests,
|
|
10278
|
+
siemEventType,
|
|
9798
10279
|
signJwt,
|
|
9799
10280
|
signedUrl,
|
|
9800
10281
|
singularize,
|
|
10282
|
+
smtpEhloHost,
|
|
9801
10283
|
spaContentSecurityPolicy,
|
|
9802
10284
|
sqlTimestamp,
|
|
9803
10285
|
storageFacade as storage,
|
|
@@ -9805,17 +10287,22 @@ export {
|
|
|
9805
10287
|
stringRule,
|
|
9806
10288
|
stripMarkdown,
|
|
9807
10289
|
temporarySignedUrl,
|
|
10290
|
+
tenantContext,
|
|
9808
10291
|
textResponse,
|
|
9809
10292
|
toHttpError,
|
|
9810
10293
|
toPaginatedResourceCollection,
|
|
9811
10294
|
toResourceCollection,
|
|
10295
|
+
traceContextStorage,
|
|
9812
10296
|
trustForwardedFor,
|
|
9813
10297
|
unregisterNamedConnection,
|
|
9814
10298
|
useSqlDialect,
|
|
9815
10299
|
validateObject,
|
|
9816
10300
|
verifyCsrfToken,
|
|
9817
10301
|
verifyJwt,
|
|
10302
|
+
webErrorResponse,
|
|
10303
|
+
webhookSignatureHeader,
|
|
9818
10304
|
whenLoaded,
|
|
10305
|
+
withDatabaseErrorHandling,
|
|
9819
10306
|
withErrorHandling,
|
|
9820
10307
|
withMiddleware,
|
|
9821
10308
|
withMigrationLock,
|