class

Hwaro::Services::Server

Inherits Reference < Object

Constants

DEBOUNCE_INTERVAL = 300.milliseconds

Debounce interval: after detecting changes, wait this long for additional changes to settle before triggering a rebuild.

MAX_BUILD_ERROR_CHARS = 4000

Cap on the build-failure text that reaches the terminal and the browser overlay. Crinja quotes the offending source lines in its message, so a single very long line near a syntax error (a minified vendor bundle inlined in a template, a generated data blob) produces a multi-megabyte message: the watcher repainted the console with it on every save — one broken template grew a serve log to 12 MB — and pushed the same payload over the live-reload socket. A few thousand characters is far more than the overlay can usefully show and still carries the error type, file and position, which is what the developer actually reads.

MAX_DEBOUNCE_ITERATIONS = 10

Maximum number of debounce iterations before forcing a rebuild. Prevents indefinite blocking when files are being written continuously.

MISSING_STAMP = {Time::UNIX_EPOCH, -1_i64, nil}

prev is the previous snapshot, used only to carry data/i18n digests forward without re-reading files whose stamps are unchanged (see watch_digest). Passing nil — the baseline scan, or a caller without a previous snapshot — computes them fresh.

MOUNT_PATH_CHARS = "-._~!$&'()*+,;=:@/"

RFC 3986 pchars besides ALPHA / DIGIT, plus the segment separator.

POLL_INTERVAL = 500.milliseconds

Polling interval for the file watcher.

UTF8_MIME_TYPES = {".html" => "text/html", ".htm" => "text/html", ".txt" => "text/plain", ".json" => "application/json", ".xml" => "application/xml", ".js" => "text/javascript", ".mjs" => "text/javascript", ".css" => "text/css", ".svg" => "image/svg+xml", ".csv" => "text/csv", ".md" => "text/markdown", ".webmanifest" => "application/manifest+json", ".map" => "application/json"}

Extensions whose served bytes are UTF-8 text, each with the base type to assume when the platform's MIME database has no opinion.

Registering the charset in the MIME table makes HTTP::StaticFileHandler emit it directly, which is the only thing that works for responses past the response output buffer (IO::DEFAULT_BUFFER_SIZE, 32 KiB) — a post-call_next header edit is too late there, so a 40 KB search.json used to lose its charset entirely. .html/.htm matter most: HTML is the site's primary content type and with --no-live-reload (or any page the injector doesn't match) it goes out through StaticFileHandler like any other file, so a 60 KB CJK page rendered as mojibake.

One list with one rule — deliberately NOT split on "does stdlib map this extension?". That question has a platform-dependent answer: Crystal seeds MIME::DEFAULT_TYPES (which already carries charsets) and then lets the OS database override it, so macOS reads /etc/apache2/mime.types and reports .md as nil while Linux reads /etc/mime.types and reports text/markdown. Keying behaviour on that split left these extensions with no charset at all on Linux — the very bug the fallback existed to fix, invisible on macOS.

WATCH_ROOTS = ["content", "templates", "static", "data", "i18n"]

The project directories the watcher always scans, in the order the serve receipt lists them. [assets] source_dir can add one more — see @extra_watch_roots.

WATCHER_IGNORE_PATTERNS = [/\.bak$/, /~$/, /\.swp$/, /\.swo$/, /\.swx$/, /\.DS_Store$/, /(?:\A|\/)\.#[^\/]+$/, /(?:\A|\/)#[^\/]+#$/, /\.tmp$/, /\.crswap$/, /___jb_tmp___$/, /___jb_old___$/, /(?:\A|\/)\.goutputstream-[^\/]+$/, /(?:\A|\/)4913$/, /(?:\A|\/)\.(?:git|obsidian|idea|vscode)\//]

Paths matching these regexes are treated as editor byproducts (backups, swap files, autosaves, OS metadata) and are excluded from the watcher. Editors using rename-based atomic save or keep-a-backup patterns (vim's default, sed -i.bak, emacs, JetBrains, …) used to double-trigger rebuilds — once for the real edit and once for the byproduct — and each event forced a full rebuild (see server.cr :full strategy fallback).

Constructors

Class methods

register_utf8_mime_types

Ensure every listed extension carries charset=utf-8 whatever the platform's database says: append to the system's base type when it has one, use ours when it does not. Never overrides a system type beyond adding the charset, never double-appends, and is idempotent — a second pass sees the charset already there and skips.

Only ever called from the dev server, so the built output and every other command keep stdlib's MIME table untouched.

Source

Instance methods

run(options : Config::Options::ServeOptions)
Source

Nested types