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

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

hydraline_server #

Part of Hydraline - real crawlable HTML for Flutter Web. Three packages, one toolkit.

hydraline (core) · hydraline_server (SSR, you are here) · hydraline_flutter (widgets)

Pure-Dart SSR - 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

Why #

SEO without a server is SSG-only — you ship static files. Add this package and you get server-side rendering: the same DocumentNode tree streams from a shelf/Dart Frog handler, with real HTTP semantics.

  • Anti-cloaking, proven. Builders physically cannot see User-Agent. Bots and humans get byte-identical HTML bodies — verified by CI test.
  • ETag, 304, cache-control. Deterministic FNV-1a hash over rendered HTML, If-None-Match revalidation, configurable TTL, cache-key query normalisation.
  • Bot-aware transport. Buffered (Content-Length) for crawlers when cache is on; chunked streaming for humans. HEAD semantics, redirects (301/302/303/307/ 308/410), X-Robots-Tag per route type.
  • HTMX helpers. First-class HtmxResponse with trigger/retarget/reswap headers, CRLF validation, fragment rendering, client-side redirect support.
  • Zero Flutter dependency. Works on any Dart server, not just behind a Flutter Web app. Use it for API doc sites, landing pages, micro-frontends.

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 / Vary - max-age from TTL, Vary: Accept-Encoding
  • X-Robots-Tag - noindex 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 #

License #

MIT - Yevhen Leonidov

0
likes
145
points
124
downloads

Documentation

API reference

Publisher

verified publisherleonidov.dev

Weekly Downloads

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

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

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

License

MIT (license)

Dependencies

hydraline, shelf

More

Packages that depend on hydraline_server