@getstrata/core 2.2.41 → 2.2.42

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 2.2.42
4
+
5
+ Generate auth JSON and forms with explicit private, no-store cache policy. Add privateJsonResponse and typed view namespace header defaults while preserving normal public defaults, native cookies, per-response overrides and HTMX representation behavior. Cover packed APIs and actual generated cookie/token/JWT/CSRF/profile/form HTTP responses.
6
+
7
+ Use the canonical error serializer for core JSON admission denials so authentication, policy/admin/ability, verification, password confirmation and body-limit failures retain no-store. Preserve exact status/message contracts, keep checker details private and retain HTML redirects/throws and successful response headers. Add packed producer regressions and a native detail-projection case.
8
+
3
9
  ## 2.2.41
4
10
 
5
11
  Make framework HTML/JSON error responses non-storeable, including configured/fallback renderers and custom-body 404 responses; retain success-response cache policy and add packed header regressions.
@@ -1,5 +1,7 @@
1
1
  import type { Middleware } from "./middleware";
2
2
  declare function jsonResponse(data: unknown, init?: ResponseInit): Response;
3
+ /** Explicit private payloads (identity, credentials and CSRF); public JSON defaults stay unchanged. */
4
+ declare function privateJsonResponse(data: unknown, init?: ResponseInit): Response;
3
5
  declare function createdResponse(data: unknown, init?: ResponseInit): Response;
4
6
  declare function noContentResponse(): Response;
5
7
  declare function errorResponse(error: unknown): Response;
@@ -9,4 +11,4 @@ declare function withJsonErrorHandling<THandler extends (request: Request) => Re
9
11
  declare function withJsonErrorHandling<TArgs extends unknown[]>(handler: (...args: TArgs) => Response | Promise<Response>): (...args: TArgs) => Promise<Response>;
10
12
  declare function createJsonErrorMiddleware(): Middleware;
11
13
  declare function createWebErrorMiddleware(): Middleware;
12
- export { createdResponse, createJsonErrorMiddleware, createWebErrorMiddleware, errorResponse, jsonResponse, noContentResponse, withErrorHandling, withJsonErrorHandling, };
14
+ export { createdResponse, createJsonErrorMiddleware, createWebErrorMiddleware, errorResponse, jsonResponse, noContentResponse, privateJsonResponse, withErrorHandling, withJsonErrorHandling, };
@@ -2,7 +2,7 @@ export type { LayoutDataResolver, RenderOptions } from "./etaViewEngine";
2
2
  export { DEFAULT_VIEWS_DIRECTORY, EtaViewEngine } from "./etaViewEngine";
3
3
  export { htmlResponse, isHtmxRequest, redirectResponse, rssResponse, textResponse, xmlResponse, } from "./htmlResponse";
4
4
  export type { ViewEngine } from "./viewEngine";
5
- export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewResponseOptions, } from "./viewRenderer";
5
+ export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewNamespaceOptions, ViewResponseOptions, } from "./viewRenderer";
6
6
  export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer } from "./viewRenderer";
7
7
  export type { WebErrorViewInput, WebErrorViewOptions } from "./webErrorView";
8
8
  export { configureWebErrorView, errorTemplateName, htmlErrorResponse, notFoundHtmlResponse, renderKernelErrorChrome, renderWebErrorHtml, } from "./webErrorView";
@@ -10,6 +10,10 @@ interface HtmxViewResponseOptions extends ViewResponseOptions {
10
10
  /** Optional fragment template; otherwise render the page template without its layout. */
11
11
  fragment?: string;
12
12
  }
13
+ interface ViewNamespaceOptions {
14
+ /** Copied response defaults. Per-render headers override defaults with the same name. */
15
+ headers?: ConstructorParameters<typeof Headers>[0];
16
+ }
13
17
  interface NamespacedViewRenderer {
14
18
  render: ViewRenderer["render"];
15
19
  renderHtmx: ViewRenderer["renderHtmx"];
@@ -20,11 +24,11 @@ declare class ViewRenderer {
20
24
  private readonly defaultLayout?;
21
25
  constructor(engine: ViewEngine, defaultLayout?: string | false | undefined);
22
26
  /** Explicit template prefix; layout names and response contracts remain unchanged. */
23
- namespace(prefix: string): NamespacedViewRenderer;
27
+ namespace(prefix: string, defaults?: ViewNamespaceOptions): NamespacedViewRenderer;
24
28
  render(name: string, data?: Record<string, unknown>, options?: ViewResponseOptions): Promise<Response>;
25
29
  /** Opt-in representation selection; auth/CSRF/tenancy remain the route's responsibility. */
26
30
  renderHtmx(name: string, data?: Record<string, unknown>, options?: HtmxViewResponseOptions): Promise<Response>;
27
31
  }
28
32
  declare const CORE_VIEW_RENDERER_TOKEN: import("../contracts/container").ServiceToken<ViewRenderer>;
29
- export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewResponseOptions };
33
+ export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewNamespaceOptions, ViewResponseOptions, };
30
34
  export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer };
@@ -2,12 +2,13 @@
2
2
  // ../../src/core/http/authorizeMiddleware.ts
3
3
  import { currentAuthUser } from "@getstrata/core/auth/authContext";
4
4
  import { ForbiddenError } from "@getstrata/core/errors/http";
5
+ import { errorResponse } from "@getstrata/core/http/response";
5
6
  function createAuthorizeMiddleware(gate, auth, resource, action) {
6
7
  return async (request, next) => {
7
8
  const user = currentAuthUser() ?? await auth.resolve(request);
8
9
  if (!gate.allows(resource, action, user)) {
9
10
  const error = new ForbiddenError;
10
- return Response.json({ error: error.message }, { status: error.status });
11
+ return errorResponse(error);
11
12
  }
12
13
  return await next();
13
14
  };
@@ -1,6 +1,7 @@
1
1
  // @bun
2
2
  // ../../src/core/http/bodySizeLimitMiddleware.ts
3
3
  import { PayloadTooLargeError } from "@getstrata/core/errors/http";
4
+ import { errorResponse } from "@getstrata/core/http/response";
4
5
  var guards = new WeakMap;
5
6
  function guardBody(request, maxBytes) {
6
7
  const existing = guards.get(request);
@@ -76,7 +77,7 @@ function createBodySizeLimitMiddleware(maxBytes = resolveMaxBodyBytes()) {
76
77
  return async (request, next) => {
77
78
  const tooLarge = () => {
78
79
  const error = new PayloadTooLargeError(`Request body exceeds the ${maxBytes} byte limit.`);
79
- return Response.json({ error: error.message }, { status: error.status });
80
+ return errorResponse(error);
80
81
  };
81
82
  const length = Number(request.headers.get("content-length"));
82
83
  if (Number.isFinite(length) && length > maxBytes)
@@ -1,7 +1,8 @@
1
1
  // @bun
2
2
  // ../../src/core/http/requireAbilityMiddleware.ts
3
3
  import { currentAuthUser } from "@getstrata/core/auth/authContext";
4
- import { ForbiddenError } from "@getstrata/core/errors/http";
4
+ import { ForbiddenError, HttpError } from "@getstrata/core/errors/http";
5
+ import { errorResponse } from "@getstrata/core/http/response";
5
6
 
6
7
  // ../../src/core/runtime/frontendMode.ts
7
8
  var FRONTEND_MODES = ["api", "server-htmx", "spa-react", "hybrid"];
@@ -80,7 +81,7 @@ function createRequireAbilityMiddleware(abilityChecker) {
80
81
  if (isViewsEnabled() && !requestPrefersJson(request)) {
81
82
  throw error;
82
83
  }
83
- return Response.json({ error: error.message }, { status: error.status });
84
+ return errorResponse(new HttpError(error.status, error.message));
84
85
  }
85
86
  throw error;
86
87
  }
@@ -1,11 +1,12 @@
1
1
  // @bun
2
2
  // ../../src/core/http/requireAuthMiddleware.ts
3
3
  import { UnauthorizedError } from "@getstrata/core/errors/http";
4
+ import { errorResponse } from "@getstrata/core/http/response";
4
5
  function createRequireAuthMiddleware(auth) {
5
6
  return async (request, next) => {
6
7
  if (!await auth.check(request)) {
7
8
  const error = new UnauthorizedError;
8
- return Response.json({ error: error.message }, { status: error.status });
9
+ return errorResponse(error);
9
10
  }
10
11
  return await next();
11
12
  };
@@ -3,6 +3,7 @@
3
3
  import { isGlobalAdmin } from "@getstrata/core/auth/accessControl";
4
4
  import { currentAuthUser } from "@getstrata/core/auth/authContext";
5
5
  import { ForbiddenError } from "@getstrata/core/errors/http";
6
+ import { errorResponse } from "@getstrata/core/http/response";
6
7
  import { logSecurityEvent } from "@getstrata/core/security/securityEvents";
7
8
  function createRequireGlobalAdminMiddleware() {
8
9
  return async (_request, next) => {
@@ -13,7 +14,7 @@ function createRequireGlobalAdminMiddleware() {
13
14
  path: new URL(_request.url).pathname
14
15
  });
15
16
  const error = new ForbiddenError("Platform admin access required.");
16
- return Response.json({ error: error.message }, { status: error.status });
17
+ return errorResponse(error);
17
18
  }
18
19
  return await next();
19
20
  };
@@ -3,6 +3,8 @@
3
3
  import { resolveUserId } from "@getstrata/core/auth/accessControl";
4
4
  import { currentAuthUser } from "@getstrata/core/auth/authContext";
5
5
  import { hasFreshPasswordConfirmation } from "@getstrata/core/auth/passwordConfirmCookie";
6
+ import { HttpError, UnauthorizedError } from "@getstrata/core/errors/http";
7
+ import { errorResponse } from "@getstrata/core/http/response";
6
8
 
7
9
  // ../../src/core/http/contentNegotiation.ts
8
10
  function requestPrefersJson(request) {
@@ -101,7 +103,7 @@ function createRequirePasswordConfirmMiddleware() {
101
103
  const user = currentAuthUser();
102
104
  if (!user) {
103
105
  if (requestPrefersJson(request)) {
104
- return Response.json({ error: "Authentication required." }, { status: 401 });
106
+ return errorResponse(new UnauthorizedError("Authentication required."));
105
107
  }
106
108
  return Response.redirect("/login", 302);
107
109
  }
@@ -110,7 +112,7 @@ function createRequirePasswordConfirmMiddleware() {
110
112
  return await next();
111
113
  }
112
114
  if (requestPrefersJson(request)) {
113
- return Response.json({ error: PASSWORD_CONFIRM_MESSAGE }, { status: 423 });
115
+ return errorResponse(new HttpError(423, PASSWORD_CONFIRM_MESSAGE));
114
116
  }
115
117
  const url = new URL(request.url);
116
118
  const intended = request.method === "GET" ? `${url.pathname}${url.search}` : "/account";
@@ -6,6 +6,8 @@ import {
6
6
  isEmailVerificationRequired
7
7
  } from "@getstrata/core/auth/emailVerification";
8
8
  import { createIntendedUrlCookieFromRequest } from "@getstrata/core/auth/intendedUrlCookie";
9
+ import { ForbiddenError } from "@getstrata/core/errors/http";
10
+ import { errorResponse } from "@getstrata/core/http/response";
9
11
 
10
12
  // ../../src/core/http/contentNegotiation.ts
11
13
  function requestPrefersJson(request) {
@@ -46,7 +48,7 @@ function createRequireVerifiedMiddleware(auth) {
46
48
  return await next();
47
49
  }
48
50
  if (requestPrefersJson(request)) {
49
- return Response.json({ error: UNVERIFIED_MESSAGE }, { status: 403 });
51
+ return errorResponse(new ForbiddenError(UNVERIFIED_MESSAGE));
50
52
  }
51
53
  const headers = new Headers({ Location: VERIFY_NOTICE_PATH });
52
54
  const intended = createIntendedUrlCookieFromRequest(request);
@@ -266,6 +266,11 @@ function jsonResponse(data, init = {}) {
266
266
  headers: init.headers
267
267
  });
268
268
  }
269
+ function privateJsonResponse(data, init = {}) {
270
+ const response = jsonResponse(data, init);
271
+ response.headers.set("Cache-Control", "private, no-store");
272
+ return response;
273
+ }
269
274
  function createdResponse(data, init = {}) {
270
275
  return jsonResponse(data, { ...init, status: init.status ?? 201 });
271
276
  }
@@ -325,6 +330,7 @@ export {
325
330
  errorResponse,
326
331
  jsonResponse,
327
332
  noContentResponse,
333
+ privateJsonResponse,
328
334
  withErrorHandling,
329
335
  withJsonErrorHandling
330
336
  };
@@ -106,7 +106,7 @@ export { createRequireAbilityMiddleware } from "../core/http/requireAbilityMiddl
106
106
  export { createRequireGlobalAdminMiddleware } from "../core/http/requireGlobalAdminMiddleware.ts";
107
107
  export { createRequireWebAuthMiddleware } from "../core/http/requireWebAuthMiddleware.ts";
108
108
  export { JsonResource, ResourceCollection, serializeDate, toPaginatedResourceCollection, toResourceCollection, whenLoaded, } from "../core/http/resources.ts";
109
- export { createdResponse, errorResponse, jsonResponse, noContentResponse, withErrorHandling, } from "../core/http/response.ts";
109
+ export { createdResponse, errorResponse, jsonResponse, noContentResponse, privateJsonResponse, withErrorHandling, } from "../core/http/response.ts";
110
110
  export type { ResourceActions, ResourceMemberHandler, ResourceRouteMap, RouteRequest, } from "../core/http/route.ts";
111
111
  export { getRouteParams, mergeRouteMaps, resourceRoutes } from "../core/http/route.ts";
112
112
  export { loginRedirectLocation, safeInternalRedirectPath, sanitizeInternalPath, } from "../core/http/safeInternalPath.ts";
@@ -171,5 +171,5 @@ export type { ValidationRule, ValidationSchema } from "../core/validation/rules.
171
171
  export { emailRule, maxLength, minLength, required, stringRule, validateObject, } from "../core/validation/rules.ts";
172
172
  export { CORE_VIEW_RENDERER_TOKEN, configureWebErrorView, configureWebLayoutData, createWebLayoutDataResolver, DEFAULT_VIEWS_DIRECTORY, EtaViewEngine, errorTemplateName, htmlErrorResponse, htmlResponse, isHtmxRequest, notFoundHtmlResponse, redirectResponse, renderKernelErrorChrome, renderWebErrorHtml, resolveWebLayoutData, rssResponse, textResponse, ViewRenderer, xmlResponse, } from "../core/view/index.ts";
173
173
  export type { ViewEngine } from "../core/view/viewEngine.ts";
174
- export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewResponseOptions, } from "../core/view/viewRenderer.ts";
174
+ export type { HtmxViewResponseOptions, NamespacedViewRenderer, ViewNamespaceOptions, ViewResponseOptions, } from "../core/view/viewRenderer.ts";
175
175
  export type { WebLayoutAuthUser, WebLayoutData, WebLayoutDataOptions, WebLayoutUserKey, } from "../core/view/webLayoutData.ts";