hydraline_server 0.0.7 copy "hydraline_server: ^0.0.7" to clipboard
hydraline_server: ^0.0.7 copied to clipboard

SSR server for Hydraline — streaming, caching, HTMX helpers. Shelf/Dart Frog middleware, bot-aware transport, ETag/304, byte-identical anti-cloaking bodies. Pairs with hydraline core. No Flutter dependency.

hydraline_server #

Pure-Dart SSR for Hydraline — streaming, caching, and HTMX helpers for shelf and Dart Frog. No Flutter dependency. Works on any Dart server.

Turn a DocumentNode tree into an HTTP response: real HTML in view-source for crawlers, chunked streaming for humans, byte-identical bodies (anti-cloaking by architecture). Add SEO to one route, keep the rest on your existing server.

pub tests

What's inside #

Module Description
hydralineMiddleware() Route-matching shelf middleware — document / hybrid / app modes, builder dispatch, redirect handling
HydralineConfig Configuration: manifest, builders, cache, TTL, bot UA pattern
DocumentBuilder Builder typedef — receives Request + matched RouteEntry as data. Architecturally UA-blind
ResponseDelivery Buffered (bots) and chunked streaming (users) from a DocumentNode tree
HydralineCache Pluggable cache interface + InMemoryCache with configurable maxSize and maxEntryBytes byte cap
Http Status helpers: redirect() (301/302/303/307/308), notFound(), gone(), withRobots(), path canonicalization
RedirectException Redirect from inside a builder — .gone() (410) and custom status
Htmx / HtmxResponse Fragment rendering, HX-Trigger, HX-Retarget, HX-Reswap, HX-Redirect with CRLF validation
HtmxTrigger Trigger helper: bare event name or {event: detail} JSON
Assets robots.txt, sitemap.xml, L0-L1 JS serving, Flutter asset injection with escaped baseHref
DartFrogAdapter Drop-in adapter for Dart Frog servers

Automatic behaviours #

  • HEAD handling — same status + headers as GET, empty body
  • ETag / 304 — deterministic 64-bit FNV-1a hash over the rendered HTML, If-None-Match revalidation (RFC 9110)
  • Cache-Control / Varymax-age from TTL, Vary: Accept-Encoding
  • X-Robots-Tagnoindex for app routes, nofollow from route metadata
  • Cache-key normalisation?a=1&b=2 and ?b=2&a=1 share one entry
  • Bot-aware transport — buffered (Content-Length) when cache is configured or bot UA matched; chunked for humans
  • Anti-cloaking — builders physically cannot see User-Agent. Byte-identical bodies verified by CI test
  • HTMX header safety — CRLF rejected at construction (response-splitting prevention)

Quick start #

import 'package:hydraline/hydraline.dart';
import 'package:hydraline_server/hydraline_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;

DocumentNode home(Request req, Object? data) => DocumentRootNode(
  head: buildHead(const SeoMeta(title: 'Home')),
  body: const [
    HeadingNode(level: 1, children: [TextNode('Home')]),
  ],
);

Future<void> main() async {
  final handler = const Pipeline()
      .addMiddleware(hydralineMiddleware(HydralineConfig(
        manifest: RouteManifest.builder()
            .route(const RouteEntry(path: '/', mode: RouteMode.document))
            .build(),
        builders: {'/': home},
        botUserAgentPattern: RegExp(r'Googlebot|bingbot'),
        cache: HydralineCache.inMemory(maxSize: 500),
        cacheTtl: const Duration(minutes: 5),
      )))
      .addHandler((req) => Response.ok('app shell'));

  // Asset endpoints: robots.txt, sitemap.xml, vanilla/htmx JS
  final assets = Assets.serveCoreAssets();

  await io.serve((req) {
    final path = req.url.path;
    if (path == 'robots.txt' || path == 'sitemap.xml' || path.endsWith('.js')) {
      return assets(req);
    }
    return handler(req);
  }, 'localhost', 8080);
}
curl -N http://localhost:8080/              # chunked streaming (humans)
curl -A Googlebot http://localhost:8080/    # buffered (bots) — same bytes
curl -I http://localhost:8080/              # HEAD → 200, etag, content-type
curl -H "If-None-Match: \"...\"" ...       # 304 Not Modified

Proven #

  • 107 unit/integration tests — route matching, cache lifecycle, ETag/304, HEAD, redirects, SSR invariants (anti-cloaking byte-identity), HTMX headers
  • SSR-invariant CI: bot vs human bodies are byte-identical — proven by test
  • Cache-key normalisation: query-parameter ordering doesn't fragment the cache
  • HEAD semantics: same status and headers as GET, empty body

Runnable example: example/main.dart — SSR, streaming, bot-aware delivery, caching and HTMX endpoint in one file.

Documentation #

  • Server Guide — full setup, streaming, HTMX, caching
  • Configuration — route manifest, SEO
  • Architecture — SSR flow, bot-aware delivery
  • Security — cloaking prevention, CSP, header injection
  • Getting Started

License #

MIT — Yevhen Leonidov

0
likes
0
points
238
downloads

Publisher

verified publisherleonidov.dev

Weekly Downloads

SSR server for Hydraline — streaming, caching, HTMX helpers. Shelf/Dart Frog middleware, bot-aware transport, ETag/304, byte-identical anti-cloaking bodies. Pairs with hydraline core. No Flutter dependency.

Homepage
Repository (GitHub)
View/report issues

Topics

#seo #ssr #shelf #htmx #flutter-web

License

unknown (license)

Dependencies

hydraline, shelf

More

Packages that depend on hydraline_server