@getstrata/core 2.2.12 → 2.2.13

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,9 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 2.2.13
4
+
5
+ - Improve view rendering and better htmx support
6
+
3
7
  ## 2.2.12
4
8
 
5
9
  - Require Bun >=1.4.3; align CI, generated/existing example runtime pins and Docker stages to 1.4.3, with bun-types >=1.4.3 behind the official @types/bun shim.
@@ -1,8 +1,10 @@
1
- export type { LayoutDataResolver } from "./etaViewEngine";
1
+ 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, ViewResponseOptions } from "./viewRenderer";
6
+ export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer } from "./viewRenderer";
5
7
  export type { WebErrorViewInput, WebErrorViewOptions } from "./webErrorView";
6
8
  export { configureWebErrorView, errorTemplateName, htmlErrorResponse, notFoundHtmlResponse, renderKernelErrorChrome, renderWebErrorHtml, } from "./webErrorView";
7
9
  export type { WebLayoutAuthUser, WebLayoutData, WebLayoutDataOptions, WebLayoutUserKey, } from "./webLayoutData";
8
- export { configureWebLayoutData, resolveWebLayoutData } from "./webLayoutData";
10
+ export { configureWebLayoutData, createWebLayoutDataResolver, resolveWebLayoutData, } from "./webLayoutData";
@@ -0,0 +1,24 @@
1
+ import type { ViewEngine } from "./viewEngine";
2
+ interface ViewResponseOptions {
3
+ layout?: string | false;
4
+ request?: Request;
5
+ status?: number;
6
+ statusText?: string;
7
+ headers?: ConstructorParameters<typeof Headers>[0];
8
+ }
9
+ interface HtmxViewResponseOptions extends ViewResponseOptions {
10
+ /** Optional fragment template; otherwise render the page template without its layout. */
11
+ fragment?: string;
12
+ }
13
+ /** App-owned renderer; request data stays in the engine's async resolver. */
14
+ declare class ViewRenderer {
15
+ private readonly engine;
16
+ private readonly defaultLayout?;
17
+ constructor(engine: ViewEngine, defaultLayout?: string | false | undefined);
18
+ render(name: string, data?: Record<string, unknown>, options?: ViewResponseOptions): Promise<Response>;
19
+ /** Opt-in representation selection; auth/CSRF/tenancy remain the route's responsibility. */
20
+ renderHtmx(name: string, data?: Record<string, unknown>, options?: HtmxViewResponseOptions): Promise<Response>;
21
+ }
22
+ declare const CORE_VIEW_RENDERER_TOKEN: import("../contracts/container").ServiceToken<ViewRenderer>;
23
+ export type { HtmxViewResponseOptions, ViewResponseOptions };
24
+ export { CORE_VIEW_RENDERER_TOKEN, ViewRenderer };
@@ -24,5 +24,7 @@ interface WebLayoutDataOptions {
24
24
  declare function configureWebLayoutData(options: WebLayoutDataOptions): void;
25
25
  declare function resetWebLayoutDataConfigForTests(): void;
26
26
  declare function resolveWebLayoutData(container: ServiceContainerLike, request?: Request, options?: WebLayoutDataOptions): Promise<Record<string, unknown>>;
27
+ /** Isolated configuration for one app; does not read legacy global defaults. */
28
+ declare function createWebLayoutDataResolver(container: ServiceContainerLike, options?: WebLayoutDataOptions): (request?: Request) => Promise<Record<string, unknown>>;
27
29
  export type { WebLayoutAuthUser, WebLayoutData, WebLayoutDataOptions, WebLayoutUserKey };
28
- export { configureWebLayoutData, resetWebLayoutDataConfigForTests, resolveWebLayoutData };
30
+ export { configureWebLayoutData, createWebLayoutDataResolver, resetWebLayoutDataConfigForTests, resolveWebLayoutData, };
@@ -169,6 +169,7 @@ export type { TracingOptions, TracingRuntime } from "../core/tracing/tracingMidd
169
169
  export { acquireTracingRuntime, createTracingMiddleware, createTracingRuntime, getTracingRuntime, } from "../core/tracing/tracingMiddleware.ts";
170
170
  export type { ValidationRule, ValidationSchema } from "../core/validation/rules.ts";
171
171
  export { emailRule, maxLength, minLength, required, stringRule, validateObject, } from "../core/validation/rules.ts";
172
- export { configureWebErrorView, configureWebLayoutData, DEFAULT_VIEWS_DIRECTORY, EtaViewEngine, errorTemplateName, htmlErrorResponse, htmlResponse, isHtmxRequest, notFoundHtmlResponse, redirectResponse, renderKernelErrorChrome, renderWebErrorHtml, resolveWebLayoutData, rssResponse, textResponse, xmlResponse, } from "../core/view/index.ts";
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, ViewResponseOptions } from "../core/view/viewRenderer.ts";
174
175
  export type { WebLayoutAuthUser, WebLayoutData, WebLayoutDataOptions, WebLayoutUserKey, } from "../core/view/webLayoutData.ts";
package/dist/index.js CHANGED
@@ -16118,6 +16118,56 @@ class EtaViewEngine {
16118
16118
  });
16119
16119
  }
16120
16120
  }
16121
+ // ../../src/core/view/viewRenderer.ts
16122
+ var HTMX_VARY_HEADERS = ["HX-Request", "HX-Boosted", "HX-History-Restore-Request"];
16123
+ function varyByHtmx(headers) {
16124
+ const existing = (headers.get("Vary") ?? "").split(",").map((field) => field.trim());
16125
+ if (existing.includes("*"))
16126
+ return;
16127
+ const fields = new Map;
16128
+ for (const field of [...existing, ...HTMX_VARY_HEADERS]) {
16129
+ if (field)
16130
+ fields.set(field.toLowerCase(), field);
16131
+ }
16132
+ headers.set("Vary", [...fields.values()].join(", "));
16133
+ }
16134
+
16135
+ class ViewRenderer {
16136
+ engine;
16137
+ defaultLayout;
16138
+ constructor(engine, defaultLayout) {
16139
+ this.engine = engine;
16140
+ this.defaultLayout = defaultLayout;
16141
+ }
16142
+ async render(name, data = {}, options = {}) {
16143
+ const html = await this.engine.render(name, data, {
16144
+ request: options.request,
16145
+ layout: options.layout ?? this.defaultLayout
16146
+ });
16147
+ const headers = new Headers(options.headers);
16148
+ headers.set("Content-Type", "text/html; charset=utf-8");
16149
+ return new Response(html, {
16150
+ status: options.status ?? 200,
16151
+ statusText: options.statusText,
16152
+ headers
16153
+ });
16154
+ }
16155
+ async renderHtmx(name, data = {}, options = {}) {
16156
+ const request = options.request ?? currentRequestMeta().request;
16157
+ if (!request)
16158
+ throw new Error("HTMX view rendering requires a request.");
16159
+ const fragment = request.headers.get("HX-Request") === "true" && request.headers.get("HX-Boosted") !== "true" && request.headers.get("HX-History-Restore-Request") !== "true";
16160
+ const headers = new Headers(options.headers);
16161
+ varyByHtmx(headers);
16162
+ return this.render(fragment ? options.fragment ?? name : name, data, {
16163
+ ...options,
16164
+ request,
16165
+ headers,
16166
+ layout: fragment ? false : options.layout
16167
+ });
16168
+ }
16169
+ }
16170
+ var CORE_VIEW_RENDERER_TOKEN = createServiceToken("core.viewRenderer");
16121
16171
  // ../../src/core/view/webLayoutData.ts
16122
16172
  var configuredLayoutOptions = {};
16123
16173
  function configureWebLayoutData(options) {
@@ -16156,6 +16206,16 @@ async function defaultLoadLayoutUser(container, _request) {
16156
16206
  }
16157
16207
  async function resolveWebLayoutData(container, request, options = {}) {
16158
16208
  const resolved = { ...configuredLayoutOptions, ...options };
16209
+ return loadWebLayoutData(container, request, resolved);
16210
+ }
16211
+ function createWebLayoutDataResolver(container, options = {}) {
16212
+ const scoped = {
16213
+ ...options,
16214
+ extra: typeof options.extra === "function" ? options.extra : { ...options.extra }
16215
+ };
16216
+ return (request) => loadWebLayoutData(container, request, scoped);
16217
+ }
16218
+ async function loadWebLayoutData(container, request, resolved) {
16159
16219
  const csrfToken = request ? resolveCsrfTokenForRequest(request) : "";
16160
16220
  const flash = request ? currentRequestMeta().flash ?? pullFlash(request) : null;
16161
16221
  const userKey = layoutUserKey(resolved);
@@ -16183,6 +16243,7 @@ export {
16183
16243
  BelongsToRelationQuery,
16184
16244
  Blueprint,
16185
16245
  CACHE_TAGS,
16246
+ CORE_VIEW_RENDERER_TOKEN,
16186
16247
  repository_default as CacheRepository,
16187
16248
  ColumnDefinition,
16188
16249
  CompositeGuard,
@@ -16256,6 +16317,7 @@ export {
16256
16317
  UnprocessableEntityError,
16257
16318
  UnsupportedSchemaFeatureError,
16258
16319
  ValidationError,
16320
+ ViewRenderer,
16259
16321
  WebFormRequest,
16260
16322
  WhereBuilder,
16261
16323
  absoluteTemporarySignedUrl,
@@ -16374,6 +16436,7 @@ export {
16374
16436
  createTrackedJob,
16375
16437
  createTransactionAcquisitionMetrics,
16376
16438
  createValidateSignatureMiddleware,
16439
+ createWebLayoutDataResolver,
16377
16440
  createdResponse,
16378
16441
  currentAuthUser,
16379
16442
  currentCredentialSource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "2.2.12",
3
+ "version": "2.2.13",
4
4
  "description": "Strata Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",