server library

Server-side half of esen_seo: bot detection and semantic HTML delivery for a shelf server.

Pure Dart without any Flutter imports — runs with dart run, in Docker or on any Dart-capable host. Bots get real semantic HTML straight in the page source, real users get the Flutter web app:

import 'package:esen_seo/server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;

import '../lib/seo_routes.dart'; // dieselbe Tabelle wie die App

void main() async {
  final handler = const Pipeline()
      .addMiddleware(seoBotMiddleware(
        routes: seoRoutes,
        siteBase: 'https://example.com',
      ))
      .addHandler(flutterAppHandler);
  await io.serve(handler, 'localhost', 8080);
}

SeoMeta, SeoSchema, SeoNode, SeoRoute and HtmlRenderer are shared with the Flutter side, so head and body render identically in both worlds.

Classes

BotDetector
Detects crawlers and preview bots by their User-Agent header.
HtmlRenderer
Renders a tree of SeoNodes into a clean, semantic HTML string.
OpenGraphMeta
OpenGraph values for link previews (Facebook, LinkedIn, WhatsApp, …).
SeoAuditPolicy
What the audit considers a problem.
SeoAuditReport
The result of one audit run.
SeoCollectionAction
Closed action vocabulary understood by both presentations.
SeoCollectionNextPage
SeoCollectionPreviousPage
SeoCollectionRecord
Prepared pure data needed to select and order one collection entry.
SeoCollectionSelectCategory
SeoCollectionSetPage
SeoCollectionSetQuery
SeoCollectionSetSort
SeoCollectionSnapshot
Result of applying SeoCollectionState to a complete data set.
SeoCollectionState
Immutable interaction state for one collection.
SeoCollectionUrlCodec
Maps one collection's closed state to namespaced query parameters.
SeoDirectoryRuntimeStore
Loads <id>.json and <id>.js from a build-owned directory.
SeoDocument
One page, produced by one read: metadata and body together, so they can never describe different records.
SeoDomFirstApplicationRuntime
One application runtime selected by a DOM-first route.
SeoDomFirstRuntimeArtifact
JavaScript that passed manifest, identity, size, and content verification.
SeoDomFirstRuntimeManifest
Describes one compiled and content-addressed application runtime.
SeoDomFirstRuntimeStore
Loads verified application runtimes for server and prerender delivery.
SeoDomFirstTabsApplicationRuntime
An application-authored transition executed by the tabs adapter.
SeoFinding
One problem, on one page.
SeoMeta
Page metadata for the document <head>: title, description, OpenGraph and Twitter Card tags.
SeoNode
A node in the semantic HTML tree that mirrors the Flutter widget tree.
SeoPage
A complete server-rendered page: head metadata plus a semantic HTML body.
SeoRedirect
A redirect from one URL to another — the target a SeoDocument has no field for.
SeoRequest
What a resolver is asked for.
SeoResolution
What a route resolves to for one concrete URL: either a page (SeoDocument) or a redirect (SeoRedirect).
SeoResolvedPage
One concrete URL of the site, resolved exactly once.
SeoRichTextSpan
One span in a declarative rich-text tree.
SeoRoute
One entry in the SEO route table — the single source of truth for a page's URL, metadata and (server-side) body.
SeoRouteMatch
A successful lookup in the route table.
SeoSchema
A Schema.org JSON-LD block for rich search results.
SeoTabsAction
A user intent understood by the shared tabs transition.
SeoTabsFirst
Selects the first tab.
SeoTabsLast
Selects the last tab.
SeoTabsNext
Selects the next tab, wrapping from the last tab to the first.
SeoTabsPrevious
Selects the previous tab, wrapping from the first tab to the last.
SeoTabsSelect
Selects one tab by index. Out-of-range indices leave selection unchanged.
SeoTabsState
The complete state needed to select one panel in a tab group.
SeoThemeAction
A closed action understood by both Flutter and DOM-first presentations.
SeoThemePersistEffect
The only external effect emitted by transitionSeoTheme.
SeoThemeRestoreAction
Restore a persisted preference, or system mode when no preference exists.
SeoThemeState
Current theme state, including the platform preference used by system mode.
SeoThemeSystemBrightnessAction
Update the platform brightness used while the preference is system.
SeoThemeToggleAction
Toggle away from the currently resolved brightness.
SeoThemeTransitionResult
Next state plus an optional declarative persistence request.
TwitterCardMeta
Twitter Card values. Title, description and image come from the OpenGraph tags — Twitter reads those automatically.

Enums

SeoCollectionSort
Sort modes supported by the closed collection interaction contract.
SeoDetail
How much of a page the caller needs.
SeoDomFirstFeature
Package-owned behaviour a DOM-first route explicitly opts into.
SeoMotionPreset
Closed, package-owned motion choices for bridge components.
SeoRedirectScope
Who a resolver-issued SeoRedirect is honoured for.
SeoRenderMode
How the semantic HTML tree is presented in the browser.
SeoRenderTarget
Where a rendered fragment is going to land.
SeoRichTextRole
Semantic roles supported by SeoRichTextSpan.
SeoRouteDelivery
Which web presentation owns a route.
SeoSeverity
How much a finding matters.
SeoThemePreference
The persisted presentation preference.

Extension Types

SeoCheck
The identity of a check, e.g. title.missing.

Constants

seoAutoInfrastructureCacheTtl → const Duration
The default for seoBotMiddleware(infrastructureCacheTtl:): derive each infrastructure file's cache lifetime from what it actually reads, rather than from one flag for the whole table.
seoCollectionMaxCategories → const int
Largest category set the progressive adapter will enhance.
seoCollectionMaxItems → const int
Largest collection the progressive adapter will enhance.
seoCollectionMaxSearchLength → const int
Largest normalized search corpus accepted for one enhanced item.
seoCollectionMaxSortKey → const int
Largest exactly representable integer shared by Dart VM and JavaScript.
seoDefaultStylesheet → const String
A minimal classless stylesheet so semantic HTML looks presentable without any work — the visual counterpart to the smart defaults. Used by default for DOM-first pages and available for SeoRenderMode.visibleShell. In the invisible default mode there is nothing to style.
seoDomFirstApplicationScriptAttribute → const String
Marks a verified application-authored runtime in a DOM-first document.
seoDomFirstAttribute → const String
Marks a semantic container that permanently owns the browser route.
seoDomFirstBootstrapScriptAttribute → const String
Marks the pre-paint theme restoration script in a generated document.
seoDomFirstCollectionStylesheet → const String
Structural styles for the compiled DOM-first collection control.
seoDomFirstRuntimeManifestSchema → const int
Current on-disk format of an application DOM-first runtime manifest.
seoDomFirstRuntimeMaxBytes → const int
Maximum accepted application runtime size before compression.
seoDomFirstRuntimeMaxGzipBytes → const int
Maximum accepted application runtime size after level-9 gzip compression.
seoDomFirstScriptAttribute → const String
Marks the package-owned DOM-first runtime in a generated document.
seoDomFirstTabsStylesheet → const String
Structural styles for the compiled DOM-first tabs control.
seoDomFirstThemeToggleStylesheet → const String
Self-contained styling for the package-owned theme toggle control.
seoInteractionRuntime → const String
Package-owned JavaScript that progressively enhances marked components in the package's visible semantic container.
seoInteractionScriptAttribute → const String
Marks the package-owned interaction runtime in a generated document.
seoInteractionStylesheet → const String
Structural styles for controls created by seoInteractionRuntime.
seoNavMaxDepth → const int
Maximum supported nesting depth for a navigation tree.
seoPieChartDefaultPaletteArgb → const List<int>
Default ARGB palette used by buildSeoPieChartNodes.
seoRichTextMaxDepth → const int
Maximum supported nesting depth for a rich-text span tree.
seoThemePreferenceStorageKey → const String
The storage key shared by the early bootstrap and browser adapter.
seoThemeToggleCompactBreakpoint → const int
Viewport width at which an opted-in theme toggle becomes icon-only.

Properties

seoMotionStylesheet String
Motion rules for components carrying a supported fixed package marker.
final

Functions

applySeoTabsTransition(SeoTabsTransition transition, SeoTabsState state, SeoTabsAction action) SeoTabsState
Executes transition within the closed state contract of a tabs control.
assertSeoHealthy(SeoAuditReport report, {SeoSeverity threshold = SeoSeverity.error}) → void
Throws SeoAuditFailure unless report passes at threshold.
auditSeoPages({required List<SeoResolvedPage> pages, required List<SeoRoute> routes, required String siteBase, SeoAuditPolicy policy = const SeoAuditPolicy(), Map<String, Object> resolverFailures = const {}}) SeoAuditReport
Audits an already-resolved set of pages.
auditSeoRoutes({required List<SeoRoute> routes, required String siteBase, List<String> additionalPaths = const [], SeoAuditPolicy policy = const SeoAuditPolicy()}) Future<SeoAuditReport>
Audits a route table for the mistakes the package cannot prevent.
boundSeoCollectionQuery(String input) String
Bounds user-entered query state before either presentation evaluates it.
buildSeoBarChartNodes({required List<SeoBarChartComponentEntry> data, String? title, double height = 220, int colorArgb = 0xFF2563EB, SeoMotionPreset motion = SeoMotionPreset.none}) List<SeoNode>
Builds the semantic mirror nodes for a bar chart.
buildSeoBreadcrumbsNodes({required List<SeoBreadcrumbComponentEntry> items, String separator = '/', String label = 'Breadcrumb'}) List<SeoNode>
Builds the semantic mirror nodes for a breadcrumb trail.
buildSeoCarouselNodes({required List<SeoCarouselComponentEntry> slides, int headingLevel = 3, String? interactionId, String interactionLabel = 'Carousel', String previousLabel = 'Previous slide', String nextLabel = 'Next slide', int initialIndex = 0}) List<SeoNode>
Builds the semantic mirror nodes for a complete carousel.
buildSeoCollectionNodes({required List<SeoCollectionComponentEntry> items, String? interactionId, String interactionLabel = 'Collection', int pageSize = 12, SeoCollectionSort initialSort = SeoCollectionSort.newest, String searchLabel = 'Search', String categoriesLabel = 'Categories', String allCategoriesLabel = 'All', String sortLabel = 'Sort', String newestLabel = 'Newest', String oldestLabel = 'Oldest', String titleLabel = 'Title', String previousLabel = 'Previous', String nextLabel = 'Next', String resultsLabel = 'results', String noResultsLabel = 'No results', String pageLabel = 'Page', bool synchronizeUrl = false}) List<SeoNode>
Builds a complete collection that can be progressively searched and paged.
buildSeoDataTableNodes({required List<String> columns, required List<List<String>> rows, String? title}) List<SeoNode>
Builds the semantic mirror nodes for a data table.
buildSeoFaqNodes({required List<SeoFaqComponentEntry> entries, String? title, int titleLevel = 2}) List<SeoNode>
Builds the semantic mirror nodes for an FAQ section.
buildSeoFigureNodes({required String src, required String alt, String? caption, int? width, int? height, bool lazy = false}) List<SeoNode>
Builds the semantic mirror nodes for an image figure.
buildSeoListViewNodes<T>({required List<T> items, required List<SeoNode> nodeBuilder(T item, int index), String listTag = 'div', String? itemTag, Map<String, String> attributes = const {}}) List<SeoNode>
Builds the semantic mirror nodes for a complete, possibly lazy list.
buildSeoNavMenuNodes<T>({required List<T> items, required SeoNavComponentItem<T> itemView(T item), String label = 'Hauptnavigation', String? interactionId}) List<SeoNode>
Builds the semantic mirror nodes for a complete navigation tree.
buildSeoPieChartNodes({required List<SeoPieChartComponentEntry> data, String? title, double diameter = 180, List<int> paletteArgb = seoPieChartDefaultPaletteArgb}) List<SeoNode>
Builds the semantic mirror nodes for a pie chart.
buildSeoRatingNodes({required double value, int max = 5, String? label}) List<SeoNode>
Builds the semantic mirror nodes for a rating.
buildSeoRichTextNodes({required List<SeoRichTextSpan> spans, String tag = 'p', Map<String, String> attributes = const {}}) List<SeoNode>
Builds semantic HTML nodes for a rich-text tree.
buildSeoStepperNodes({required List<SeoCarouselComponentEntry> steps, int headingLevel = 3, String? interactionId, String interactionLabel = 'Steps', String previousLabel = 'Back', String nextLabel = 'Next', String positionLabel = 'Step', int initialIndex = 0}) List<SeoNode>
Builds the complete ordered semantic source for a stepper.
buildSeoTabsNodes({required List<SeoCarouselComponentEntry> tabs, int headingLevel = 3, String? interactionId, String interactionLabel = 'Tabs', int initialIndex = 0}) List<SeoNode>
Builds the semantic mirror nodes for a tab group.
buildSeoTestimonialNodes({required String quote, String? author, String? role, String? sourceUrl}) List<SeoNode>
Builds the semantic mirror nodes for a testimonial.
buildSeoThemeToggleNodes({String lightLabel = 'Light', String darkLabel = 'Dark', String lightSemanticLabel = 'Use light theme', String darkSemanticLabel = 'Use dark theme', bool compactOnSmallScreens = false}) List<SeoNode>
Builds the inert marker upgraded by the package-owned theme runtime.
cssColorArgb(int argb) String
Formats an ARGB integer as lowercase CSS hexadecimal.
cssNumber(double value) String
Formats a finite CSS number without an unnecessary decimal suffix.
cssPercent(double value, double total) String
Formats value's share of total as a percentage with one decimal at most.
finishSeoResolution(SeoResolution resolution, {required String path, String? canonicalBase, void onWarning(String path, String warning)?}) SeoResolution
The chokepoint. Every path from a resolver to any output — SSR, prerender, sitemap.xml, llms.txt — runs its resolution through here, exactly as every tag and attribute runs through the renderer's policy. A resolver is a new road from (eventually CMS) data into HTTP output, and the mistake this package already made once was to put a policy in one output path while another bypassed it.
initialSeoTabsState({required int count, int index = 0}) SeoTabsState
Returns a valid initial state for count tabs.
isValidSeoApplicationRuntimeId(String id) bool
Whether id is safe as a logical identity and artifact file component.
isValidSeoCollectionSortKey(int sortKey) bool
Whether sortKey has identical integer semantics on VM and JavaScript.
isValidSeoInteractionId(String value) bool
Whether value is safe to use as a package interaction identifier.
loadSeoDomFirstRuntime(SeoDomFirstRuntimeStore store, SeoDomFirstApplicationRuntime reference) Future<SeoDomFirstRuntimeArtifact>
Loads a runtime and rejects stores that return a different identity.
matchSeoRoute(List<SeoRoute> routes, String path) SeoRouteMatch?
Finds the first route in routes matching path, or null.
normalizeSeoCollectionPageSize(int pageSize) int
Keeps page sizes useful and bounds the amount shown in one DOM mutation.
normalizeSeoCollectionText(String input) String
Normalizes human text identically in Flutter and compiled JavaScript.
normalizeSeoPath(String path) String
Normalizes a path: leading slash, no trailing slash, ''/.
pagesForGenerator({required String canonicalBase, List<SeoRoute>? routes, List<SeoResolvedPage>? pages, List<String> additionalPaths = const [], SeoDetail detail = SeoDetail.head}) List<SeoResolvedPage>
The shared front door of the synchronous generators (seoSitemapXml, seoLlmsTxt): resolve routes, or validate and return a pre-resolved pages snapshot.
parseSeoCollectionSort(String value) SeoCollectionSort?
Parses a package-owned sort marker.
parseSeoThemePreference(String? value) SeoThemePreference
Parses the closed storage representation. Invalid values use system mode.
prerenderSite({required List<SeoRoute> routes, required String siteBase, String buildDir = 'build/web', List<String> additionalPaths = const [], bool writeSitemap = true, bool writeRobotsTxt = true, bool writeLlmsTxt = true, bool write404Page = true, String? indexNowKey, SeoRenderMode renderMode = SeoRenderMode.seoOnly, String? stylesheet, String? domFirstStylesheet = seoDefaultStylesheet, bool enableInteractions = false, String? interactionNonce, SeoDomFirstRuntimeStore? domFirstRuntimeStore, int concurrency = 8, void onSkipped(String path, SeoResolution resolution)?, void onError(String path, Object error, StackTrace stack)?}) Future<List<String>>
Bakes the SEO route table into the built Flutter web app as static HTML files — for hosting without a Dart server (Firebase Hosting, GitHub Pages, any CDN).
resolveSeoPages({required List<SeoRoute> routes, String? canonicalBase, List<String> additionalPaths = const [], SeoDetail detail = SeoDetail.full, bool enumerateRoutePaths = true, int concurrency = 8, void onError(String path, Object error, StackTrace stack)?, void onWarning(String path, String warning)?, bool debugCheckMetaStability = false}) Future<List<SeoResolvedPage>>
Enumerates every concrete URL the table stands for and resolves each one once, asynchronously.
resolveSeoPagesSync({required List<SeoRoute> routes, String? canonicalBase, List<String> additionalPaths = const [], SeoDetail detail = SeoDetail.head, bool enumerateRoutePaths = true}) List<SeoResolvedPage>
The synchronous engine behind the sync generators (seoSitemapXml, seoLlmsTxt).
safeChartValue(double value) double
Normalizes chart input data so invalid and negative values become zero.
safeDimension(double value, double fallback) double
Normalizes a positive dimension, falling back for invalid input.
selectSeoCollection({required List<SeoCollectionRecord> records, required int categoryCount, required int pageSize, SeoCollectionState state = const SeoCollectionState()}) SeoCollectionSnapshot
Computes the canonical result for state without mutating records.
seoBotMiddleware({List<SeoRoute>? routes, SeoPageResolver? resolve, String? siteBase, bool serveSitemap = true, bool serveRobotsTxt = true, bool serveLlmsTxt = true, String? indexNowKey, List<String> additionalSitemapPaths = const [], bool unknownRoutesAs404 = true, BotDetector detector = const BotDetector(), SeoRedirectScope applyResolverRedirects = SeoRedirectScope.all, String? domFirstStylesheet = seoDefaultStylesheet, String? domFirstNonce(Request request)?, SeoDomFirstRuntimeStore? domFirstRuntimeStore, Duration? infrastructureCacheTtl = seoAutoInfrastructureCacheTtl, void onResolveError(String path, Object error, StackTrace stack)?}) → Middleware
Shelf middleware that serves semantic HTML to bots and passes real users through to the wrapped handler (usually the Flutter web build).
seoCollectionCategoryIndexes(List<String> categories, List<String> categoryLabels) List<int>
Maps one entry's labels to the stable indexes from categoryLabels.
seoCollectionCategoryLabels(Iterable<List<String>> groups) List<String>
Derives stable display categories, deduplicated by normalized label.
seoCollectionSortMarker(SeoCollectionSort sort) String
Stable marker serialized into package-owned collection markup.
seoDomFirstFeatureBootstrapScriptHtml(Set<SeoDomFirstFeature> features, {String? nonce}) String
Returns the package-owned pre-paint bootstrap for selected features.
seoDomFirstFeatureScriptHtml(Set<SeoDomFirstFeature> features, {String? nonce}) String
Returns the compiled runtime needed by the selected DOM-first features.
seoDomFirstFeatureStyleHtml(Set<SeoDomFirstFeature> features, {String? nonce}) String
Returns the style tag needed by the selected DOM-first features.
seoInteractionScriptHtml({String? nonce}) String
Wraps the trusted runtime in an inline script tag.
seoInteractionStyleHtml({String? nonce}) String
Wraps the package-owned interaction CSS in a managed style tag.
seoLlmsFullTxt({required String siteBase, List<SeoRoute>? routes, List<SeoResolvedPage>? pages, String? title, String? description, List<String> additionalPaths = const [], int concurrency = 8}) Future<String>
Generates an llms-full.txt: like seoLlmsTxt, but with the complete page content inlined as markdown — AI assistants get the whole site in one request instead of crawling page by page.
seoLlmsTxt({required String siteBase, List<SeoRoute>? routes, List<SeoResolvedPage>? pages, String? title, String? description, List<String> additionalPaths = const []}) String
Generates an llms.txt from the SEO route table.
seoRedirectMiddleware({String? canonicalHost, bool forceHttps = false, bool trustProxy = false, bool stripTrailingSlashes = true, Map<String, String> redirects = const {}}) → Middleware
Shelf middleware for SEO-relevant 301 redirects — duplicate content under several URLs splits ranking signals, one canonical URL per page collects them.
seoRobotsTxt({required String siteBase, bool includeSitemap = true}) String
Generates a robots.txt that allows all crawlers and announces the sitemap.
seoSitemapXml({required String siteBase, List<SeoRoute>? routes, List<SeoResolvedPage>? pages, List<String> additionalPaths = const []}) String
Generates a sitemap.xml from the SEO route table.
serializeSeoThemePreference(SeoThemePreference preference) String?
Serializes a preference for storage; system mode removes the stored value.
submitIndexNow({required String siteBase, required String key, required List<String> paths, Uri? endpoint, String? keyLocation, Duration timeout = const Duration(seconds: 10)}) Future<bool>
Submits URLs to search engines via the IndexNow protocol (https://www.indexnow.org — Bing, Seznam, Naver, Yandex share the index; Google crawls the classic way via sitemap).
transitionSeoCollection(SeoCollectionState state, SeoCollectionAction action, {required List<SeoCollectionRecord> records, required int categoryCount, required int pageSize}) SeoCollectionState
Applies one closed action and returns canonical collection state.
transitionSeoTabs(SeoTabsState state, SeoTabsAction action) SeoTabsState
Computes the next tab state without retaining state or performing effects.
transitionSeoTheme(SeoThemeState state, SeoThemeAction action) SeoThemeTransitionResult
Applies one closed theme action without retaining state or doing I/O.

Typedefs

SeoBarChartComponentEntry = ({String label, double value})
Pure input for one bar in buildSeoBarChartNodes.
SeoBodyBuilder = FutureOr<List<SeoNode>> Function(Map<String, String> params)
Builds the semantic HTML body for a matched route — used by the SSR server. May be asynchronous (e.g. to load content from a database).
SeoBreadcrumbComponentEntry = ({String label, String? url})
Pure input for one step in buildSeoBreadcrumbsNodes.
SeoCarouselComponentEntry = ({String label, List<SeoNode> nodes})
Pure input for one slide in buildSeoCarouselNodes.
SeoCollectionComponentEntry = ({List<String> categories, List<SeoNode> nodes, String searchText, int sortKey, String title})
Pure input for one entry in buildSeoCollectionNodes.
SeoCollectionUrlValues = ({String? category, String? page, String? query, String? sort})
Canonical encoded values for one collection's URL parameters.
SeoFaqComponentEntry = ({String answer, String question})
Pure input for one question in buildSeoFaqNodes.
SeoMetaBuilder = SeoMeta Function(Map<String, String> params)
Builds the SeoMeta for a matched route.
SeoNavComponentItem<T> = ({List<T> children, String label, String? url})
A pure view of an application-specific navigation item.
SeoPageResolver = FutureOr<SeoPage?> Function(Request request)
Maps an incoming request to the SeoPage a bot should receive, or null to fall through to the regular Flutter app.
SeoPathEnumerator = FutureOr<List<String>> Function()
Lists every concrete URL a :param route stands for, so the sitemap, llms.txt and the prerenderer can enumerate a pattern route. For /products/:slug it returns the actual product paths.
SeoPieChartComponentEntry = ({int? colorArgb, String label, double value})
Pure input for one segment in buildSeoPieChartNodes.
SeoResolver = FutureOr<SeoResolution> Function(SeoRequest request)
Produces the SeoResolution for one concrete URL — metadata and body from a single read. The content source of a SeoRoute.dynamic.
SeoStepperComponentEntry = ({String label, List<SeoNode> nodes})
Pure input for one step in buildSeoStepperNodes.
SeoTabComponentEntry = ({String label, List<SeoNode> nodes})
Pure input for one panel in buildSeoTabsNodes.
SeoTabsTransition = SeoTabsState Function(SeoTabsState state, SeoTabsAction action)
A state-free tabs transition shared by platform presentations.

Exceptions / Errors

SeoAuditFailure
Thrown by assertSeoHealthy when a report does not pass.