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
Narrow an unknown value to string, defaulting to "" if not a string.
Replaces typeof x === "string" ? x : "" at type boundaries.
Resource management pattern (like Haskell's bracket or try-with-resources). Ensures cleanup happens even if the operation throws.
Split an array into chunks of a given size
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
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"
Compact format for table cells: "yyyy-MM-dd HH:mm" in the given timezone.
Delegates to the browser-compatible formatIsoForPreview helper so the
same formatting runs on the server and in the admin JS bundle.
Format an error context into a human-readable activity log message
Format an error detail string with request context and error 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 request ID, or empty string if outside request context
True when running inside a runWithPendingWork scope (i.e. a request).
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.
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)
Strip non-numeric characters from a phone number and normalize to +{prefix}{local}
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
Lazy evaluation - compute once on first call, cache forever.
Use instead of let x = null; const getX = () => x ??= compute();
Compose functions left-to-right (pipe) 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.
Reset the registry (for testing)
Run a function within a pending-work scope
Run a function with a request-scoped random ID for log correlation
Set module-level debug log suppression (avoids env race in parallel tests).
Set module-level request log suppression (avoids env race in parallel tests).
Non-mutating sort with comparator
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.
A single cache's stats snapshot
Collection cache returned by collectionCache()
Error log context (privacy-safe metadata only)
-
attendeeId: number
Optional: attendee ID
-
code: ErrorCodeType
Error code for classification
-
detail: string
Optional: additional safe context
-
eventId: number
Optional: event ID (not slug)
| { ok: false; error: string; }
Result type for price validation
Slug-with-index pair
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)
Join an array of strings into a single string (curried reduce shorthand). Replaces the common pattern: reduce((acc: string, s: string) => acc + s, "")
Usage
import * as mod from "docs/utilities.ts";