@getstrata/core 0.5.59 → 0.5.61
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 +17 -0
- package/README.md +2 -2
- package/dist/core/database/bindConnection.d.ts +1 -3
- package/dist/core/database/bunSql.d.ts +13 -0
- package/dist/core/http/contentSecurityPolicy.d.ts +27 -0
- package/dist/core/http/memoryThrottleMiddleware.d.ts +2 -1
- package/dist/core/http/requestMetaContext.d.ts +1 -0
- package/dist/core/http/safeInternalPath.d.ts +4 -0
- package/dist/core/http/securedRouteModelBinding.d.ts +2 -0
- package/dist/core/http/securityHeadersMiddleware.d.ts +5 -2
- package/dist/core/http/webErrorResponse.d.ts +1 -1
- package/dist/core/view/etaViewEngine.d.ts +3 -2
- package/dist/core/view/htmlResponse.d.ts +5 -2
- package/dist/core/view/index.d.ts +4 -1
- package/dist/core/view/viewEngine.d.ts +1 -0
- package/dist/core/view/webErrorView.d.ts +21 -0
- package/dist/entries/database/bunSql.js +1 -0
- package/dist/entries/http/contentSecurityPolicy.js +1 -0
- package/dist/entries/http/memoryThrottleMiddleware.js +10 -2
- package/dist/entries/http/requireWebAuthMiddleware.js +34 -2
- package/dist/entries/http/response.js +133 -555
- package/dist/entries/http/safeInternalPath.js +38 -0
- package/dist/entries/http/securedRouteModelBinding.js +27 -5
- package/dist/entries/http/securityHeadersMiddleware.js +33 -60
- package/dist/entries/http/webErrorResponse.js +132 -554
- package/dist/entries/logging/requestLoggingMiddleware.js +2 -1
- package/dist/entries/view.js +96 -9
- package/dist/framework/public-api.d.ts +7 -3
- package/dist/index.js +665 -384
- package/package.json +17 -2
- package/dist/config/contentSecurityPolicy.d.ts +0 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/logging/requestLoggingMiddleware.ts
|
|
3
|
-
import { runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
|
|
3
|
+
import { currentRequestMeta, runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
|
|
4
4
|
|
|
5
5
|
// ../../src/core/http/clientIp.ts
|
|
6
6
|
function trustForwardedFor(env = process.env) {
|
|
@@ -57,6 +57,7 @@ var appLogger = new Logger("app");
|
|
|
57
57
|
function createRequestLoggingMiddleware() {
|
|
58
58
|
return async (request, next) => {
|
|
59
59
|
return await runWithRequestMeta({
|
|
60
|
+
...currentRequestMeta(),
|
|
60
61
|
ipAddress: readClientIp(request) ?? null,
|
|
61
62
|
userAgent: request.headers.get("user-agent"),
|
|
62
63
|
request
|
package/dist/entries/view.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/view/etaViewEngine.ts
|
|
3
3
|
import { join, relative } from "path";
|
|
4
|
+
import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
|
|
4
5
|
import { Eta } from "eta";
|
|
5
6
|
|
|
6
7
|
// ../../src/core/view/assertEtaHtmlSource.ts
|
|
@@ -198,7 +199,8 @@ class EtaViewEngine {
|
|
|
198
199
|
}
|
|
199
200
|
async render(name, data = {}, options = {}) {
|
|
200
201
|
const template = name.endsWith(".eta") ? name : `${name}.eta`;
|
|
201
|
-
const
|
|
202
|
+
const request = options.request ?? currentRequestMeta().request;
|
|
203
|
+
const layoutData = this.resolveLayoutData ? await this.resolveLayoutData(request) : {};
|
|
202
204
|
const mergedData = { ...layoutData, ...data };
|
|
203
205
|
const body = await this.eta.renderAsync(template, mergedData);
|
|
204
206
|
const layout = options.layout ?? DEFAULT_LAYOUT;
|
|
@@ -213,6 +215,9 @@ class EtaViewEngine {
|
|
|
213
215
|
}
|
|
214
216
|
}
|
|
215
217
|
// ../../src/core/view/htmlResponse.ts
|
|
218
|
+
function withCharset(contentType) {
|
|
219
|
+
return contentType.includes("charset=") ? contentType : `${contentType}; charset=utf-8`;
|
|
220
|
+
}
|
|
216
221
|
function htmlResponse(html, init = {}) {
|
|
217
222
|
return new Response(html, {
|
|
218
223
|
status: init.status ?? 200,
|
|
@@ -233,9 +238,6 @@ function redirectResponse(location, status = 302) {
|
|
|
233
238
|
}
|
|
234
239
|
});
|
|
235
240
|
}
|
|
236
|
-
function notFoundHtmlResponse(body = "Not Found") {
|
|
237
|
-
return htmlResponse(body, { status: 404 });
|
|
238
|
-
}
|
|
239
241
|
function textResponse(body, init = {}) {
|
|
240
242
|
return new Response(body, {
|
|
241
243
|
status: init.status ?? 200,
|
|
@@ -248,13 +250,91 @@ function xmlResponse(body, init = {}) {
|
|
|
248
250
|
return new Response(body, {
|
|
249
251
|
status: init.status ?? 200,
|
|
250
252
|
headers: {
|
|
251
|
-
"Content-Type": "application/xml
|
|
253
|
+
"Content-Type": withCharset(init.contentType ?? "application/xml")
|
|
252
254
|
}
|
|
253
255
|
});
|
|
254
256
|
}
|
|
257
|
+
function rssResponse(body, init = {}) {
|
|
258
|
+
return xmlResponse(body, { ...init, contentType: "application/rss+xml" });
|
|
259
|
+
}
|
|
260
|
+
// ../../src/core/view/webErrorView.ts
|
|
261
|
+
import { currentRequestMeta as currentRequestMeta2 } from "@getstrata/core/http/requestMetaContext";
|
|
262
|
+
var configuredErrorView = {};
|
|
263
|
+
function configureWebErrorView(options) {
|
|
264
|
+
configuredErrorView = { ...options };
|
|
265
|
+
}
|
|
266
|
+
function escapeHtml(value) {
|
|
267
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
268
|
+
}
|
|
269
|
+
function renderKernelErrorChrome(input) {
|
|
270
|
+
const title = escapeHtml(input.title);
|
|
271
|
+
const message = escapeHtml(input.message);
|
|
272
|
+
const errorLines = Object.entries(input.errors ?? {}).flatMap(([field, messages]) => messages.map((item) => `${field}: ${item}`)).map((line) => `<li>${escapeHtml(line)}</li>`).join("");
|
|
273
|
+
const details = errorLines ? `<ul class="error-list">${errorLines}</ul>` : "";
|
|
274
|
+
const goBack = input.status === 422 ? `<p><a href="javascript:history.back()">Go back</a></p>` : "";
|
|
275
|
+
return `<!doctype html>
|
|
276
|
+
<html lang="en">
|
|
277
|
+
<head>
|
|
278
|
+
<meta charset="UTF-8" />
|
|
279
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
280
|
+
<title>${title}</title>
|
|
281
|
+
<link rel="stylesheet" href="/assets/app.css" />
|
|
282
|
+
</head>
|
|
283
|
+
<body>
|
|
284
|
+
<header class="site-header">
|
|
285
|
+
<a class="brand" href="/">Home</a>
|
|
286
|
+
</header>
|
|
287
|
+
<main class="site-main">
|
|
288
|
+
<section class="page-header">
|
|
289
|
+
<h1>${title}</h1>
|
|
290
|
+
<p>${message}</p>
|
|
291
|
+
${details}
|
|
292
|
+
${goBack}
|
|
293
|
+
</section>
|
|
294
|
+
</main>
|
|
295
|
+
</body>
|
|
296
|
+
</html>
|
|
297
|
+
`;
|
|
298
|
+
}
|
|
299
|
+
function errorTemplateName(status) {
|
|
300
|
+
if (status === 404) {
|
|
301
|
+
return "errors/not-found";
|
|
302
|
+
}
|
|
303
|
+
if (status === 403) {
|
|
304
|
+
return "errors/forbidden";
|
|
305
|
+
}
|
|
306
|
+
return "errors/error";
|
|
307
|
+
}
|
|
308
|
+
async function renderWebErrorHtml(input) {
|
|
309
|
+
const render = configuredErrorView.render;
|
|
310
|
+
if (!render) {
|
|
311
|
+
return renderKernelErrorChrome(input);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
return await render({
|
|
315
|
+
...input,
|
|
316
|
+
request: input.request ?? currentRequestMeta2().request
|
|
317
|
+
});
|
|
318
|
+
} catch {
|
|
319
|
+
return renderKernelErrorChrome(input);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async function htmlErrorResponse(input) {
|
|
323
|
+
return htmlResponse(await renderWebErrorHtml(input), { status: input.status });
|
|
324
|
+
}
|
|
325
|
+
async function notFoundHtmlResponse(body) {
|
|
326
|
+
if (body !== undefined) {
|
|
327
|
+
return htmlResponse(body, { status: 404 });
|
|
328
|
+
}
|
|
329
|
+
return htmlErrorResponse({
|
|
330
|
+
status: 404,
|
|
331
|
+
title: "Not Found",
|
|
332
|
+
message: "The page you requested was not found."
|
|
333
|
+
});
|
|
334
|
+
}
|
|
255
335
|
// ../../src/core/view/webLayoutData.ts
|
|
256
336
|
import { currentAuthUser } from "@getstrata/core/auth/authContext";
|
|
257
|
-
import { currentRequestMeta as
|
|
337
|
+
import { currentRequestMeta as currentRequestMeta4 } from "@getstrata/core/http/requestMetaContext";
|
|
258
338
|
|
|
259
339
|
// ../../src/core/contracts/serviceTokens.ts
|
|
260
340
|
var CORE_CONFIG_TOKEN = "core.config";
|
|
@@ -351,7 +431,7 @@ var requestMetaContext = createAsyncContextStore("@getstrata/requestMetaContext"
|
|
|
351
431
|
function runWithRequestMeta(meta, callback) {
|
|
352
432
|
return requestMetaContext.run(meta, callback);
|
|
353
433
|
}
|
|
354
|
-
function
|
|
434
|
+
function currentRequestMeta3() {
|
|
355
435
|
return requestMetaContext.getStore() ?? {
|
|
356
436
|
ipAddress: null,
|
|
357
437
|
userAgent: null
|
|
@@ -433,7 +513,7 @@ function verifyCsrfToken(request, submittedToken) {
|
|
|
433
513
|
return Bun.CSRF.verify(submittedToken, csrfVerifyOptions());
|
|
434
514
|
}
|
|
435
515
|
function resolveCsrfTokenForRequest(request) {
|
|
436
|
-
const metaToken =
|
|
516
|
+
const metaToken = currentRequestMeta3().csrfToken;
|
|
437
517
|
if (metaToken) {
|
|
438
518
|
return metaToken;
|
|
439
519
|
}
|
|
@@ -578,7 +658,7 @@ async function defaultLoadLayoutUser(container, _request) {
|
|
|
578
658
|
async function resolveWebLayoutData(container, request, options = {}) {
|
|
579
659
|
const resolved = { ...configuredLayoutOptions, ...options };
|
|
580
660
|
const csrfToken = request ? resolveCsrfTokenForRequest(request) : "";
|
|
581
|
-
const flash = request ?
|
|
661
|
+
const flash = request ? currentRequestMeta4().flash ?? pullFlash(request) : null;
|
|
582
662
|
const userKey = layoutUserKey(resolved);
|
|
583
663
|
const loadUser = resolved.loadUser ?? defaultLoadLayoutUser;
|
|
584
664
|
const user = await loadUser(container, request);
|
|
@@ -587,18 +667,25 @@ async function resolveWebLayoutData(container, request, options = {}) {
|
|
|
587
667
|
[userKey]: user,
|
|
588
668
|
csrfToken,
|
|
589
669
|
flash,
|
|
670
|
+
cspNonce: currentRequestMeta4().cspNonce ?? "",
|
|
590
671
|
...extra
|
|
591
672
|
};
|
|
592
673
|
}
|
|
593
674
|
export {
|
|
594
675
|
DEFAULT_VIEWS_DIRECTORY,
|
|
595
676
|
EtaViewEngine,
|
|
677
|
+
configureWebErrorView,
|
|
596
678
|
configureWebLayoutData,
|
|
679
|
+
errorTemplateName,
|
|
680
|
+
htmlErrorResponse,
|
|
597
681
|
htmlResponse,
|
|
598
682
|
isHtmxRequest,
|
|
599
683
|
notFoundHtmlResponse,
|
|
600
684
|
redirectResponse,
|
|
685
|
+
renderKernelErrorChrome,
|
|
686
|
+
renderWebErrorHtml,
|
|
601
687
|
resolveWebLayoutData,
|
|
688
|
+
rssResponse,
|
|
602
689
|
textResponse,
|
|
603
690
|
xmlResponse
|
|
604
691
|
};
|
|
@@ -24,7 +24,8 @@ export type { ServiceProvider } from "../core/contracts/di.ts";
|
|
|
24
24
|
export { resolveService } from "../core/contracts/di.ts";
|
|
25
25
|
export type { DatabaseConnection } from "../core/database/baseRepository.ts";
|
|
26
26
|
export { default as BaseRepository } from "../core/database/baseRepository.ts";
|
|
27
|
-
export { bindDatabaseConnection } from "../core/database/bindConnection.ts";
|
|
27
|
+
export { bindDatabaseConnection, getBoundDatabaseConnection, resetBoundDatabaseConnection, } from "../core/database/bindConnection.ts";
|
|
28
|
+
export { bindBunSql, createBunSqlPool } from "../core/database/bunSql.ts";
|
|
28
29
|
export { createDatabaseConnection } from "../core/database/connection.ts";
|
|
29
30
|
export { getActiveDatabaseConnection, runWithDatabaseConnection, } from "../core/database/connectionContext.ts";
|
|
30
31
|
export { getDefaultDatabasePool, getDefaultDatabaseQuery, registerDefaultDatabasePool, } from "../core/database/defaultConnection.ts";
|
|
@@ -55,6 +56,8 @@ export { auth, cache, config, events, log, mail, policyGate, queue, storage, } f
|
|
|
55
56
|
export { createBodySizeLimitMiddleware } from "../core/http/bodySizeLimitMiddleware.ts";
|
|
56
57
|
export { readClientIp, trustForwardedFor } from "../core/http/clientIp.ts";
|
|
57
58
|
export { conditionalJsonResponse } from "../core/http/conditionalResponse.ts";
|
|
59
|
+
export type { ContentSecurityPolicyDirectives, ContentSecurityPolicyOptions, } from "../core/http/contentSecurityPolicy.ts";
|
|
60
|
+
export { configureContentSecurityPolicy, generateCspNonce, resolveContentSecurityPolicy, resolveHtmlContentSecurityPolicy, serverHtmxContentSecurityPolicy, spaContentSecurityPolicy, strictApiContentSecurityPolicy, } from "../core/http/contentSecurityPolicy.ts";
|
|
58
61
|
export { readBunRequestCookie, readRequestCookie } from "../core/http/cookies.ts";
|
|
59
62
|
export { createCorsMiddleware } from "../core/http/corsMiddleware.ts";
|
|
60
63
|
export { createCsrfMiddleware } from "../core/http/csrfMiddleware.ts";
|
|
@@ -65,7 +68,7 @@ export { createFlashMiddleware } from "../core/http/flashMiddleware.ts";
|
|
|
65
68
|
export { FormRequest } from "../core/http/formRequest.ts";
|
|
66
69
|
export { applyMiddlewareToRoutes, bindRouteModel, buildRequestCacheKey, composeMiddleware, createAuthMiddleware, createAuthorizeMiddleware, createRequireAuthMiddleware, paginatedResponse, parsePaginationQuery, parsePositiveIntParam, requestIdMiddleware, securedBindRouteModel, securedBindRouteModelByKey, withMiddleware, wrapRouteHandler, } from "../core/http/index.ts";
|
|
67
70
|
export { createLoginThrottleMiddleware } from "../core/http/loginThrottleMiddleware.ts";
|
|
68
|
-
export { createMemoryThrottleMiddleware } from "../core/http/memoryThrottleMiddleware.ts";
|
|
71
|
+
export { createMemoryThrottleMiddleware, resetMemoryThrottleForTests, } from "../core/http/memoryThrottleMiddleware.ts";
|
|
69
72
|
export { createMetricsMiddleware, normalizeMetricPath } from "../core/http/metricsMiddleware.ts";
|
|
70
73
|
export type { Middleware, RouteHandler } from "../core/http/middleware.ts";
|
|
71
74
|
export { type ParsedUpload, parseMultipartUpload } from "../core/http/parseMultipartUpload.ts";
|
|
@@ -77,6 +80,7 @@ export { createRequireWebAuthMiddleware } from "../core/http/requireWebAuthMiddl
|
|
|
77
80
|
export { serializeDate, toPaginatedResourceCollection, toResourceCollection, } from "../core/http/resources.ts";
|
|
78
81
|
export { createdResponse, jsonResponse, noContentResponse, withErrorHandling, } from "../core/http/response.ts";
|
|
79
82
|
export type { RouteRequest } from "../core/http/route.ts";
|
|
83
|
+
export { loginRedirectLocation, safeInternalRedirectPath, sanitizeInternalPath, } from "../core/http/safeInternalPath.ts";
|
|
80
84
|
export { createScimThrottleMiddleware } from "../core/http/scimThrottleMiddleware.ts";
|
|
81
85
|
export { createSecurityHeadersMiddleware } from "../core/http/securityHeadersMiddleware.ts";
|
|
82
86
|
export { createThrottleMiddleware } from "../core/http/throttleMiddleware.ts";
|
|
@@ -118,6 +122,6 @@ export { currentTraceId, runWithTraceContext } from "../core/tracing/traceContex
|
|
|
118
122
|
export { createTracingMiddleware } from "../core/tracing/tracingMiddleware.ts";
|
|
119
123
|
export type { ValidationRule, ValidationSchema } from "../core/validation/rules.ts";
|
|
120
124
|
export { emailRule, maxLength, minLength, required, stringRule, validateObject, } from "../core/validation/rules.ts";
|
|
121
|
-
export { configureWebLayoutData, DEFAULT_VIEWS_DIRECTORY, EtaViewEngine, htmlResponse, isHtmxRequest, notFoundHtmlResponse, redirectResponse, resolveWebLayoutData, textResponse, xmlResponse, } from "../core/view/index.ts";
|
|
125
|
+
export { configureWebErrorView, configureWebLayoutData, DEFAULT_VIEWS_DIRECTORY, EtaViewEngine, errorTemplateName, htmlErrorResponse, htmlResponse, isHtmxRequest, notFoundHtmlResponse, redirectResponse, renderKernelErrorChrome, resolveWebLayoutData, rssResponse, textResponse, xmlResponse, } from "../core/view/index.ts";
|
|
122
126
|
export type { ViewEngine } from "../core/view/viewEngine.ts";
|
|
123
127
|
export type { WebLayoutAuthUser, WebLayoutData, WebLayoutDataOptions, WebLayoutUserKey, } from "../core/view/webLayoutData.ts";
|