Files
Zakaria El OrcheandClaude Sonnet 5 4feabc45d5 fix(ext-view): clear cross-engine error, propagate globals across JteExtension builder chain
- BaseViewExtension: wrap the ViewRuntimeBridge provider collision with a message
  naming the real constraint (one view engine per FlashApp) instead of the generic
  "duplicate provider" error.
- BaseViewExtension/JteExtension: carry registered globals across JteExtension's
  immutable settings builders (templateRoot/serveStatics/staticPrefix/withStaticCors/
  staticCors). addGlobal() called before any of those used to be silently dropped,
  since each builder method returned a fresh instance with an empty globals list.
- Correct flash-ext-view-jte docs (README, architecture.md, model-and-globals.md):
  globals merge flat with one typed @param per key, not under a global.* namespace
  like Thymeleaf — the docs previously claimed the same reserved namespace for both
  engines, which doesn't match JteRuntime's actual merge behavior.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 13:31:21 +00:00

122 lines
2.9 KiB
Markdown

# flash-ext-view-jte
Opinionated jte SSR extension for Flash.
This module keeps jte semantics front and center:
- `JteExtension`
- `JteHandler`
- `@Template`
- `ViewModel` from `flash-ext-view-core`
- globals merged flat into the model, one typed `@param` per global
The extension mirrors `gg.jte.ContentType` into Flash HTTP content type:
- `gg.jte.ContentType.Html` -> `text/html`
- `gg.jte.ContentType.Plain` -> `text/plain`
## Static serving (opinionated)
Static serving is enabled by default and serves classpath assets from `/static/**`
(`src/main/resources/static/**`) on an HTTP wildcard route.
Defaults:
- `serveStatics = true`
- `staticPrefix = "/static"`
- ETag via CRC32 + length
- `304 Not Modified` on `If-None-Match`
- pre-compressed `.gz` support when client sends `Accept-Encoding: gzip`
- MIME auto-resolution for common web/media/font/wasm types
- cache policy:
- versioned assets (`file.<hash>.ext` or query `?v=...` / `?hash=...`) -> `public, max-age=31536000, immutable`
- non-versioned -> `no-cache`
- byte-range support for large files (threshold configurable, default 64 KiB)
If classpath `/static` is missing, extension logs a warning and disables static routes.
### Configuration examples
```java
// option 1: customizer constructor
app.install(new JteExtension(cfg -> cfg
.templateRoot("/templates")
.serveStatics(true)
.staticPrefix("/assets")));
// option 2: fluent style
app.install(new JteExtension()
.templateRoot("/templates")
.withStaticServing()
.staticPrefix("/assets"));
```
Disable static serving:
```java
app.install(new JteExtension().serveStatics(false));
```
Enable static CORS (default disabled):
```java
app.install(new JteExtension().staticCors(cfg -> cfg
.enableStaticCors(true)
.staticCorsAllowOrigin("*")
.staticCorsAllowMethods("GET,HEAD,OPTIONS")
.staticCorsAllowHeaders("*")
.staticCorsMaxAge(3600)));
```
## Quick Start
```java
import jte.view.ext.dev.relism.flash.JteExtension;
FlashApp.create(8080)
.
install(new JteExtension(cfg ->cfg
.
templateRoot("/templates")
.
contentType(gg.jte.ContentType.Html)))
.
scan("com.example.web")
.
startAndBlock();
```
```java
import core.view.ext.dev.relism.flash.ViewModel;
import jte.view.ext.dev.relism.flash.JteHandler;
import jte.view.ext.dev.relism.flash.Template;
import models.dev.relism.flash.Request;
import routing.dev.relism.flash.GET;
@GET("/")
@Template("pages/home.jte")
public final class HomePage extends JteHandler {
@Override
public ViewModel render(Request req) {
return ViewModel.empty()
.with("page", new HomePageModel("Flash + jte", "elorc"))
.with("build", "dev");
}
public record HomePageModel(String title, String author) {
}
}
```
## Docs
- `docs/architecture.md`
- `docs/handlers.md`
- `docs/model-and-globals.md`
- `docs/performance.md`