Shared utilities: FP helpers, formatting, slugs, caching, and logging.
Functional Programming
Curried utilities for data transformation:
pipe, filter, map, reduce, compact, unique, and more.
Formatting
Currency formatting, phone normalization, markdown rendering, and timezone-aware date/time display.
Caching
TTL and LRU caches with a global registry for admin stats.
Queue a promise that must complete before the response is sent
Create a bounded LRU (Least Recently Used) cache. Evicts the least-recently-used entry when capacity is reached. Uses a doubly-linked list for O(1) LRU tracking so that get() does not mutate the key-value index.
Resource management pattern (like Haskell's bracket or try-with-resources). Ensures cleanup happens even if the operation throws.
Create an in-memory collection cache with TTL. Loads all items via fetchAll on first access or after invalidation/expiry, then serves from memory until the TTL expires or invalidate() is called. Accepts an optional clock function for testing.
Remove null and undefined values from array
Create a request timer for measuring duration
Create a failed result
Curried filter
Curried flatMap
Await all queued work. Call before returning the response.
Format an amount in minor units (pence/cents) as a currency string. e.g. formatCurrency(1050) → "£10.50" (when currency is GBP)
Format a UTC ISO datetime string for display in the given timezone. Returns e.g. "Monday 15 June 2026 at 14:00 BST"
Format an error context into a human-readable activity log message
Generate a random slug with at least 2 digits and 2 letters. Uses Fisher-Yates shuffle on the fixed positions to avoid bias.
Generate a unique slug by retrying until one is not taken.
Collect stats from all registered caches
Get the number of decimal places for a currency code
Get the current theme ("light" or "dark")
Group array items by a key function
Identity function
Check if value is not null or undefined
Check if a naive datetime-local string is a parseable datetime. Does not interpret timezone — purely a format check.
Validate that a string is a valid IANA timezone identifier.
Resettable lazy reference - like once() but can be reset for testing. Returns [get, set] tuple where set(null) resets to uncomputed state.
Load currency code from settings into sync-accessible state. Called once per request in routes/index.ts before templates render. Settings are already cached so this is cheap on repeat calls.
Load theme from settings into sync-accessible state. Called once per request in routes/index.ts before templates render. Settings are already cached so this is cheap on repeat calls.
Convert a naive datetime-local value (YYYY-MM-DDTHH:MM) to a UTC ISO string, interpreting the value as local time in the given timezone.
Log a debug message with category prefix For detailed debugging during development
Log a classified error to console.error and persist to the activity log. Console output uses error codes and safe metadata (never PII). Activity log entry is encrypted and visible to admins on the log pages.
Log a classified error to console.error only (no ntfy, no activity log). Use this where calling logError would cause infinite recursion (e.g. ntfy.ts).
Log a completed request to console.debug Path is automatically redacted for privacy
Curried map
Map over a promise-returning function in parallel (Promise.all)
Map over a promise-returning function sequentially (one at a time)
Strip non-numeric characters from a phone number, then prefix if it starts with 0
Normalize a user-provided slug: trim, lowercase, replace spaces with hyphens
Current time as a Date
Full ISO-8601 timestamp for created/logged_at fields
Epoch milliseconds for numeric comparisons
Create a successful result
Lazy evaluation - compute once on first call, cache forever.
Use instead of let x = null; const getX = () => x ??= compute();
Pick specific keys from an object
Compose functions left-to-right (pipe) Uses recursive conditional types for arbitrary-length type safety.
Async pipe - compose async functions left-to-right Each function receives the awaited result of the previous one. Uses recursive conditional types for arbitrary-length type safety.
Redact dynamic segments from paths for privacy-safe logging Replaces:
Curried reduce
Register a cache stat provider (called at module load time)
Render markdown to HTML (block-level: paragraphs, lists, etc.). Raw HTML is escaped.
Render markdown to inline HTML (no wrapping <p> tags). Raw HTML is escaped.
Reset the registry (for testing)
For testing: reset the currency code to default
For testing: reset the theme to default
Run a function within a pending-work scope
Run a function with a request-scoped random ID for log correlation
For testing: set the currency code directly
For testing: set the theme directly
Non-mutating sort with comparator
Sort by a key or getter function
Get today's date as YYYY-MM-DD in the given timezone.
Convert minor units to major units string for form display. e.g. toMajorUnits(1050) → "10.50" (for GBP)
Convert major units (decimal) to minor units (integer). e.g. toMinorUnits(10.50) → 1050 (for GBP)
Create a TTL (Time-To-Live) cache. Entries expire after ttlMs milliseconds. Accepts an optional clock function for testing.
Remove duplicate values (by reference/value equality)
Remove duplicates by a key function
Convert a UTC ISO datetime string to a datetime-local input value (YYYY-MM-DDTHH:MM) in the given timezone. Used for pre-populating form inputs with timezone-adjusted values.
Validate and convert a raw price string to minor units. Returns ok with 0 if raw is empty and minPrice is 0 (pay-what-you-want with no input). Returns error if raw is empty and minPrice > 0, or if parsed value is out of range.
Validate a normalized slug. Returns error message or null.
Log categories for debug logging
Result type for price validation
Result type for operations that can fail with a Response
Default timezone when none is configured
Error code strings for use in logError calls
Human-readable labels for error codes (shown in admin activity log)
Usage
import * as mod from "docs/utilities.ts";