Ch-ch-ch-ch-ch-changes!

Track updates, bug fixes, and new features as we build the New York State Death Index

Changelog

All notable changes to the New York State Death Index project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[96436a7] - 2025-12-17 (v1.4.4)

Added

  • Cache statistics plugin - New server/plugins/cache-stats.ts logs filesystem cache statistics (entries, size, hit rate) on startup and periodically; warns when high-cardinality endpoints leak into filesystem cache
  • Memory metrics tracking - New server/utils/metrics/ folder with comprehensive Sentry metrics for memory, cache, search, and Solr performance
  • Sentry documentation - Comprehensive Sentry audit report, fixes applied, and metrics implementation docs in docs/ folder

Changed

  • CRITICAL: LRU cache for high-cardinality endpoints - Person pages (/api/people/[id]) and surname pages (/api/people/surnames/[surname]) now use bounded LRU cache (max 10K and 5K entries respectively) instead of unbounded filesystem cache, preventing memory exhaustion from cache key tracking
  • LRU cache configuration - Added cachePerson and cacheSurname storage mounts in nuxt.config.ts using Unstorage's lruCache driver with automatic eviction of least-used entries

Fixed

  • Memory management plugin async/await - Fixed TypeScript error where logMemoryUsage() used await without being declared as async function; properly wrapped async operations in setInterval callbacks to avoid promise misuse warnings

Technical

  • LRU provides bounded memory: 50K URLs requested → max 10K kept → oldest auto-evicted → predictable memory usage
  • Cache stats plugin monitors for Person/Surname entries in filesystem cache (indicates LRU bypass)
  • Memory management plugin now uses async/await pattern throughout with proper promise handling
  • Async functions wrapped in void-returning callbacks for setInterval to satisfy ESLint no-misused-promises rule
  • Comprehensive metrics system tracks memory (heap, RSS, GC), cache (files, namespaces), search (patterns, complexity, zero-results), and Solr (query time, response size, errors)

[5ad6084] - 2025-12-11 (v1.4.3)

Changed

  • CRITICAL: Disabled SWR across entire API - Removed stale-while-revalidate from all 24 remaining API endpoints to eliminate background revalidation memory pressure
  • Caching strategy shift - Now relying primarily on Cloudflare CDN edge caching with long cache headers; server-side caching serves as fallback only

Technical

  • All API endpoints now use swr: false - eliminated hundreds of concurrent background revalidation operations
  • Static historical data doesn't benefit from background freshness checks; CDN-first strategy is more appropriate
  • Expected 80-90% reduction in server-side fetch operations and associated memory overhead

[b6c1a8a] - 2025-12-11 (v1.4.2)

Changed

  • CRITICAL: Aggressive cache duration reduction - Person pages cache reduced from 30 days → 7 days (75% reduction in cache key accumulation), person duplicates reduced to 7 days, surnames reduced to 14 days
  • Disabled SWR for high-cardinality endpoints - Removed stale-while-revalidate from person pages, duplicates, and surnames to eliminate background revalidation memory pressure

Technical

  • Person page cache keys will now expire 4x faster, significantly reducing the unbounded cache key Set growth that causes OOM crashes
  • Without SWR, cached pages serve until expiry then fetch fresh (no background revalidation creating additional memory overhead)

[115873d] - 2025-12-10 (v1.4.1)

Added

  • Memory management plugin - New server/plugins/memory-management.ts proactively manages memory in production with periodic garbage collection (every 15 minutes), memory usage logging, and emergency GC when heap exceeds 1.5GB
  • Memory leak diagnosis documentation - Comprehensive analysis in docs/memory-leak-diagnosis.md explaining Nitro cache key tracking issue, implemented fixes, and future optimization recommendations

Fixed

  • CRITICAL: OOM crashes from cache key accumulation - Nitro's filesystem cache was tracking all cache keys in memory indefinitely; added Node.js memory flags (--max-old-space-size=2048 --expose-gc) to production start script, doubling available memory and enabling proactive garbage collection
  • Memory leak in API auth plugin - Fixed server/plugins/api-internal-auth.ts to prevent multiple wrapping of globalThis.$fetch and reduce closure scope to minimize memory retention

Changed

  • Production memory allocation - Increased Node.js heap limit from ~1GB to 2GB via NODE_OPTIONS in start script
  • README improvements - Enhanced introduction, added hosting details for Digital Ocean/CloudFlare, clarified SSR/prerendering strategy

Technical

  • Memory management plugin logs structured metrics (heapUsedMB, rssMB) to BetterStack for monitoring
  • Plugin warns when heap usage exceeds 1.5GB and automatically triggers garbage collection
  • Added documentation to nuxt.config.ts explaining filesystem cache driver limitations (keys still tracked in memory)
  • API auth plugin now uses globalThis instead of global per ESLint standards
  • Memory plugin only runs in production (no dev overhead)

[edaf6c8] - 2025-12-08 (v1.4)

Added

  • YouTubeEmbed component - New lazy-loading YouTube video embed component that only loads the iframe when user clicks play, improving initial page load performance

Changed

  • Lawsuit page video embed - Replaced direct iframe with lazy-loaded YouTubeEmbed component for Court of Appeals oral arguments video

Fixed

  • CRITICAL: API caching OOM crashes - All 31 API endpoints now explicitly use base: 'cacheFileSystem' in their cache configuration to prevent out-of-memory crashes in production
  • Cache key filename issues - All cache key generation changed from hyphens to underscores as separators, fixing Nitro's automatic hyphen-stripping behavior that was causing cache key collisions

Technical

  • Nitro storage configuration renamed from cache to cacheFileSystem with documentation about proper usage
  • All API cache keys now use underscores for spaces and separators instead of hyphens
  • YouTube thumbnail domain (i.ytimg.com) added to CSP img-src for lazy-loaded embeds
  • CSS styling added for YouTube embed component in components.css
  • README updated to document server/plugins/ structure

[7890889] - 2025-12-06 (v1.3.1)

Changed

  • API security bypasses - API host check middleware now skips security validation for internal Nuxt icon routes (/api/_nuxt_icon/) and Sentry tunnel (/api/sentry-tunnel) since these have their own security mechanisms

[1686fb7] - 2025-12-06 (v1.3)

Added

  • Internal API authentication - New server/plugins/api-internal-auth.ts adds a secret header to SSR fetch calls, allowing the API middleware to distinguish legitimate server requests from curl/scrapers

Changed

  • Enhanced API security - Rewrote api-host-check.ts middleware to use Sec-Fetch-Site header for validating browser requests, block direct URL access to API routes, and require internal auth secret for SSR requests
  • Name sanitization whitelist - Changed nameSanitizer.ts from blacklist to whitelist approach, only allowing letters, spaces, hyphens, apostrophes, and asterisks (wildcards) - prevents Solr query injection from special characters like +
  • Person page duplicates loading - Changed from useAsyncData to useLazyAsyncData so duplicate record checks don't block initial page render
  • GitHub Actions improvements - Enhanced Cloudflare and Sentry workflows with better summaries and output formatting

Technical

  • Added apiInternalSecret to nuxt.config.ts runtime config for SSR-to-API authentication
  • API now validates requests using combination of Sec-Fetch-Site, Referer, and internal auth headers
  • Removed unused variable destructuring in person page

[8d4349e] - 2025-12-05 (v1.2)

Added

  • GPLv3 license file - Added LICENSE.md with full GPLv3 license text
  • Global error handler plugin - New server/plugins/error-handler.ts catches unhandled errors from SWR background revalidation, unhandled promise rejections, and uncaught exceptions with full request context (IP, referrer, user agent)
  • Sentry client-side error filtering - Added ignoreErrors config to filter out benign errors: View Transition aborts, ResizeObserver loops, network failures, browser extension errors

Changed

  • Replaced all console.error with structured logging - All 49 server-side console.error calls now use appropriate Pino loggers (logSolrError, logExternalApiError, logWarning, etc.) for better log aggregation and filtering
  • Removed misleading fromCache property - Handler-level cache detection wasn't working with filesystem caching; removed from all API handlers and logger interfaces
  • Cache detection via timing - Added cacheStatus field to API logger based on response duration (< 15ms = hit, < 30ms = likely, else miss)
  • README updated - Added documentation for logging system, global error handler, and Sentry filtering; updated file structure to include server/plugins/

Technical

  • Added warning logs for specific 404 scenarios in coordinates, states, and person endpoints to track data quality issues
  • Solr client and error handler now use structured logging instead of console.error

[32b9608] - 2025-12-05 (v1.1)

Added

  • OG Image composables - New app/composables/og-image/ folder with 5 composables for generating customized Open Graph images per page type (person, place, surname, state, and static pages)
  • GitHub config - Added .github/ folder with funding config and workflows for Cloudflare and Sentry

Changed

  • Version bump to 1.1
  • Chart.js dynamic imports - Charts now load via dynamic imports for better code splitting and reduced initial bundle size
  • Footer data client-side only - BlueskyFeed and NewsletterPastIssues components now use server: false to prevent payload bloat (social/newsletter data doesn't need SSR)
  • OG images runtime generation - Dynamic pages (places, people, surnames, states) now generate OG images at runtime instead of prerendering, ensuring correct data in images
  • README updated - Directory tree now includes OG image composables folder and placeSlugsConfig.ts

Fixed

  • Broken /legal link - Fixed link in Legal.vue that pointed to non-existent /legal path (now correctly links to /lawsuit)
  • V.A. Medical Ctr place mapping - Added slug mapping for "V.A. Medical Ctr, Canandaigua" to handle comma in facility name
  • OG image custom fonts - OgImageBase now uses inline styles for font-family (Satori renderer doesn't process Tailwind font classes)

Technical

  • Nuxt config: disabled crawlLinks for prerendering to speed up builds, added failOnError: false, configured Young Serif and Roboto fonts for OG images
  • All pages now use explicit imports for OG image composables
  • Removed unused ogStats computed property from person page
  • Cleaned up duplicate auto-import warnings by removing redundant re-export files

[0d4f89d] - 2025-12-05 (v1.0 🎉)

Added

  • Geographic meta tags - Person and place pages now include Open Graph geo meta tags (place:location:latitude, place:location:longitude) and legacy geo meta tags (geo.position, ICBM) for improved geographic search relevance
  • Schema.org structured data - Place pages include Place or AdministrativeArea JSON-LD schema with geo coordinates, containment hierarchy, and optional Wikipedia links
  • Site-wide Schema.org - Default layout includes WebSite schema with spatialCoverage set to New York State
  • Missing location narrative - Person pages now explain when death location is missing/illegible, with specific notes about faded microfiche years (1942-1943, 1949, 1940s-1950s) and the NYS DOH transcription project
  • Death year embargo indicator - Certificate ordering section now shows whether death is within the 50-year restricted period for NYS death certificates
  • Gender-as-name display - Quick search results now show [Female] Smith or [Male] Jones when a person has no given name recorded, instead of showing nothing
  • BetterStack Errors integration - Sentry tunnel now optionally dual-forwards error data to BetterStack's Sentry-compatible endpoint (configurable via BETTERSTACK_ERRORS_ENABLED)
  • Place slug special mappings - New placeSlugsConfig.ts for places with ampersands, parentheses, or unusual formatting (e.g., "NYS Home for Vet & Depen", "Mohawk Valley (Utica) Psy Ctr")
  • Bluesky types - New bluesky.ts type definitions for feed components and API responses
  • Cloudflare cache purge script - New scripts/purge-cloudflare-cache.sh runs after DigitalOcean builds to purge CDN cache

Changed

  • Version bump to 1.0 - Official 1.0 release! 🎉
  • Site URL - Updated canonical URL to https://www.NewYorkDeathIndex.com (with www prefix)
  • Certificate ordering layout - "Additional Information" section now uses responsive three-column grid with better NYC vs State explanation
  • Certificate ordering copy - Improved explanations about NYC death certificates, pre-1898 consolidation records, and local clerk search suggestions
  • Search API optimization - Added explicit field list (fl parameter) to prevent OOM errors on large result sets; only fetches fields needed for display and maps
  • Surname API query strategy - Non-hyphenated surnames now search both record_surname AND record_surnames fields (some records only have one populated); hyphenated surnames still use exact match only
  • Surname API age stats - Max age query now runs separately from total count to avoid filtering affecting the record count
  • Surname API field lists - Map data and oldest people queries now request only needed fields to prevent memory issues with large surnames
  • Search results table - Removed virtualization, restructured pagination outside scrollable container for better UX
  • README overhaul - Comprehensive update with expanded features list, tech stack details, monitoring/analytics section, and updated prerequisites (Node.js 22+)
  • NYC geo handling - Person pages use borough center coordinates (not neighborhood) for NYC deaths/residences in geo meta tags
  • Place API URL encoding - All place name parameters now properly URL-encoded for special characters
  • Social media config - Bluesky and Twitter usernames now configurable via environment variables
  • CSP comments - Standardized capitalization in Content Security Policy comments
  • Build memory - Increased from 4GB to 8GB for Sentry source map processing

Fixed

  • Home page caching - Added SWR and cache headers to index route (was missing, unlike other pages)
  • About page caching - Added SWR and cache headers (was missing)
  • URL encoding in API calls - PersonSimilarRecords now properly encodes place names with special characters

Technical

  • Sentry tunnel refactored with extensive documentation, child logger for monitoring, and dual-forwarding architecture
  • New CSS: .info-box-three-columns-grid with responsive breakpoints for tablet (2-col) and mobile (1-col)
  • New runtime config: betterStackEnabled, betterStackEndpoint, newsletterSourceIdentifier, socialMediaBlueSkyUsername, socialMediaTwitterUsername
  • DigitalOcean build command now includes Cloudflare cache purge step
  • Dependencies: @sentry/nuxt 10.29.0, @antfu/eslint-config 6.4.1, eslint 9.39.1, and others updated
  • Removed unused Twitter widget type declarations
  • Cleaned up default layout (removed commented-out footer code)

[b7d7ecc] - 2025-12-02

Changed

  • Footer links - "open data" now links to Archive.org collection of source data files; "Donate" label expanded to "Donate to Reclaim The Records"

[49f8e16] - 2025-12-02 (v0.9.9)

Added

  • YouTube video embed - Embedded Court of Appeals oral arguments video on the lawsuit page with responsive aspect ratio
  • Surname links - Surname mentions in PersonSimilarRecords now link to their corresponding surname pages

Changed

  • Page title system - Refactored to fix duplicate site name issue; usePageTitles composable now returns only page-specific part, @nuxtjs/seo titleTemplate handles appending site name with // separator

Fixed

  • Page title duplication - Browser tabs no longer show site name twice (e.g., "Page // Site Name | Site Name")

Technical

  • YouTube domains (youtube.com, youtube-nocookie.com) added to CSP frame-src for video embeds
  • titleTemplate: '%s // %siteName' in nuxt.config handles consistent title formatting across all pages
  • Removed hardcoded site name suffixes from 6 page files (lawsuit, search, about, changelog, counties index, surname pages)

[686656d] - 2025-12-02 (v0.9.8)

Added

  • Donation prompt - Slide-in notification in bottom-right corner encouraging donations to Reclaim The Records, appears after 45 seconds or 50% page scroll
  • Donate button in navbar - Prominent orange donate button with heart icon in header navigation (desktop and mobile)
  • Wikipedia override system - New config file (wikipediaOverridesConfig.ts) to override incorrect Solr Wikipedia links for specific places (e.g., facilities linking to towns instead of their own articles)
  • Version-based prompt reset - Donation prompt timers automatically reset when a new version is deployed, so returning users see it again after updates

Changed

  • API logging exclusions - Logger now skips internal Nuxt routes (_nuxt_icon, _nuxt), Sentry tunnel, and successful Bluesky feed calls to reduce noise
  • Donation prompt timing - Session-based + 48-hour cooldown (won't nag within same session, waits 48 hours before showing again in new session)

Technical

  • DonationPrompt component uses localStorage (dismissed/shown timestamps) and sessionStorage (session flag) for smart display logic
  • App version injected into runtime config from package.json at build time for deployment detection
  • Wikipedia overrides applied in PersonNarrative, PersonRawData, and place API endpoint
  • Renamed isBot to isBotUserAgent to avoid conflict with @nuxtjs/robots module
  • Removed duplicate exports from apiLogger.ts to fix Nuxt auto-import warnings

[8c79b38] - 2025-12-02 (v0.9.7)

Added

  • Server-side logging system - New Pino-based logging infrastructure for structured server logs (outputs to BetterStack in production)
  • Search query logging - Logs all search queries with source differentiation (homepage vs advanced), result counts, and timing
  • Record lookup logging - Person, place, and surname lookups logged with detailed metadata (name parts, IDs, years, counties, NYC detection, cache status)
  • Newsletter logging - Signup attempts and past issues fetches logged with status, timing, and error details
  • Not-found error logging - Comprehensive 404 logging with request context (IP, referrer domain, user-agent, bot detection)
  • Residents-who-left logging - Logs residence data lookups (people who lived somewhere but died elsewhere)
  • Surname soundalike logging - Logs when surnames aren't found and how many soundalike suggestions were returned

Changed

  • Sentry search tracking - Renamed 'natural-language' to 'homepage' for consistency with server-side logging terminology
  • Page title template - Set to null to prevent duplicate site name appearing in browser tab titles

Fixed

  • Mailchimp TypeScript error - Added type guard for campaign list response union type
  • Residence data terminology - Clarified that residence counts are "residents who died elsewhere" not "total residents"

Technical

  • Pino logger with pretty-printing in development, JSON output in production
  • Modular logger architecture: index.ts, searchLogger.ts, apiLogger.ts, errorLogger.ts, newsletterLogger.ts, recordLogger.ts, notFoundLogger.ts
  • Request context extraction utilities for IP (with X-Forwarded-For support), referrer domain parsing, user-agent
  • Bot detection for filtering crawler traffic in logs
  • Child logger pattern for module-specific logging with consistent base configuration
  • API middleware enhanced to log request context (IP, referrer, user-agent) on all API calls

[41c683b] - 2025-12-02 (v0.9.6)

Added

  • NYC residence handling - Person pages now handle NYC residents specially, showing borough name (e.g., "the Bronx") instead of specific neighborhood when person was a NYC resident at time of death
  • NYC residence InfoBox - Blue InfoBox explains when NYC residents died outside the city, their records were included in state index with residence information
  • Bluesky social feed - Footer now displays recent posts from @reclaimtherecords.org via Bluesky's free public API (original posts only, no reposts)
  • Wikipedia links in narratives - Person page narratives now include "Learn more about..." links to Wikipedia for both death and residence locations
  • DOH comments for counties - County pages now display NYS Department of Health comments when available (previously only shown for localities)
  • Twitter feed component - Created TwitterFeed component (currently disabled due to API rate limits, preserved for future use)
  • Parentheses handling in place names - Added support for facility names with parentheses (e.g., "Mohawk Valley (Utica) Psy Ctr")

Changed

  • NYC residence narrative - NYC residents now show "the Bronx (Bronx County), in New York City, New York" instead of specific neighborhood names
  • NYC residence map markers - For NYC residents, map now uses borough center coordinates instead of neighborhood coordinates, with tooltip explaining "Borough center - specific neighborhood not shown"
  • NYC residence raw data - Original data section omits neighborhood name, type, and coordinates for NYC residents; shows borough center coordinates and Wikipedia instead
  • Person narrative possessive pronouns - NYC residence InfoBox uses gender-specific pronouns (his/her/their) for more natural language
  • Wikipedia link formatting - Single location shows entire sentence as link; multiple locations show individual location names as separate links
  • Place page structure - Removed duplicate grey borders between sections (chart/towns and statistics)

Fixed

  • Duplicate Wikipedia links - Person narratives no longer show duplicate Wikipedia links when death and residence locations are the same
  • Place slug parentheses - URLs with parentheses now properly encode/decode and match Solr data (e.g., facility names)
  • Server-side capitalization - Place slugs with parentheses now properly capitalize letters after opening parenthesis (e.g., "(Utica)" not "(utica)")
  • Double border CSS - Removed redundant place-section-with-divider from chart and towns sections to prevent stacked borders

Technical

  • Bluesky API integration: /server/api/bluesky/feed.ts endpoint with 15-minute caching and SWR
  • BlueskyFeed component fetches and displays posts server-side (SSR-friendly, SEO-optimized)
  • Enhanced CSP: Added Twitter and Bluesky domains for script-src, img-src, connect-src, frame-src
  • NYC detection uses both server-side flags (record_locations_residence_nyc) and client-side fallback (isNYCCounty)
  • Borough name mapping with article handling ("the Bronx" vs other boroughs)
  • Place slug normalization middleware preserved for future use
  • Twitter widgets TypeScript declarations added (types/twitter.d.ts)
  • Image extraction from Bluesky embeds (app.bsky.embed.images#view type)
  • Bluesky feed filters out reposts and replies, showing only original posts

[v0.9.5] - 2025-11-07

Added

  • Duplicate record detection - Person pages now detect and display when multiple versions of the same death certificate exist in the index (matched by year + certificate number)
  • Dual certificate field support - Duplicate detection handles both record_state_file_number (1957-2017) and record_certificate_number (1880-1956) fields
  • Dirty data filtering - Automatically excludes certificate numbers containing question marks (illegible/transcription errors from faded microfiche)
  • Duplicate record info box - Shows warning with links to alternate versions of certificates, useful for detecting corrections, re-issues, or name changes (e.g., "Matt Jones" → "Miedysław Jonasz")
  • Duplicate records analysis page - New /people/duplicates/ page with year-based navigation showing duplicate certificates for data quality analysis
  • Year-based duplicate browsing - Expandable year cards showing duplicate count per year, with on-demand loading of duplicate details
  • Chunked duplicate loading - Years with many duplicates load in batches of 100 with real-time progress indicator (e.g., "Loading 100/1286...")
  • Card grid layout for duplicates - Responsive card grid (up to 5 across) for displaying duplicate certificate groups with white background and shadow
  • Compact duplicate display - Certificate-based grouping with slash-separated names, showing dates only when they differ
  • Duplicates stylesheet - New duplicates.css with scoped Tailwind-based styles for the duplicates analysis page
  • Duplicates API endpoint - New /api/people/duplicates endpoint with three modes: specific certificate lookup (year+fileNumber), year summary (no params), or year details with pagination (yearOnly+offset+limit params)
  • Problematic location code detection - System now detects when 4-digit location codes don't match official NY State Gazetteer and infers county from first 2 digits
  • Inferred location display - Person pages, search results, and maps now show inferred counties for ~10,000 records with unrecognized location codes
  • NYC certificate detection in search - Search results tables and home page quick results now show "probably Brooklyn" etc. for detected NYC certificates
  • County code data - Added official 2-digit county codes to counties.json for all 62 NY counties
  • useEffectiveLocations composable - New reusable composable for determining effective death/residence locations (handles geocoded, NYC, suggested, and problematic codes)
  • inferredLocationDisplay utility - Lightweight utility for search results to detect and display inferred locations
  • problematicLocationCodes utility - Functions to detect problematic codes and extract county information

Changed

  • Search autocomplete behavior - Added 300ms debouncing to all three location typeahead inputs (death, residence, geographic) for smoother typing
  • Multi-word place name search - Autocomplete now properly handles phrases like "White Plains" by querying first word and filtering results server-side
  • Person page hero section - NYC certificate locations now show "probably Manhattan, New York County" instead of raw certificate numbers
  • Similar Records search - Records with inferred counties now use county center coordinates for geographic radius search (10-100 miles) instead of county-only filter
  • Map tooltips - Distinguished between NYC certificate inferences ("Inferred from likely NYC certificate") vs fuzzy matches ("Based on fuzzy match")
  • Map legend text - Shows "Approximate" only for the specific locations that are approximate/inferred (not both when only one is)
  • Chart location labels - Inferred locations show "Ontario County" not "Ontario County center" in chart titles
  • Search results display - Inferred locations show "town unknown" in place column and "probably Ontario" in county column to avoid redundancy

Fixed

  • Problematic age filtering - Records with impossible ages (e.g., 190 years old) now excluded from all age statistics: "oldest resident" displays, average age calculations, and surname age statistics
  • Problematic age detection - Person pages now properly check record_age_problematic field and show both tooltip and prominent orange InfoBox warning for impossible ages
  • 'Unknown' surname filtering - Fixed bug where 'unknown' surname was appearing in most common surnames lists on both the main surnames page and place/town pages; now properly filtered in both endpoints
  • Place autocomplete pluralization - "1 resident" instead of "1 residents" in PlaceResidentsWhoLeft component
  • Conflicting narrative text - Person pages no longer show both "death location not recorded" AND "recorded with code X" for same person
  • Search results sticky headers - Table headers now properly stick to top while scrolling through results
  • Search results pagination scroll - Clicking pagination now scrolls table back to top automatically
  • County sorting in search - "probably Ontario" sorts as "Ontario" (ignores "probably" prefix during alphabetical sort)

Technical

  • New types: CountyInfo, ProblematicLocationInfo, InferredLocationDisplay interfaces
  • Person page reduced from 665 to 587 lines via composable extraction
  • Multi-strategy scroll detection for virtualized table pagination
  • Dark mode detection via MutationObserver for Chart.js legend text (in progress)
  • Enhanced map component to accept effectiveResidenceCounty, effectiveResidencePlace, and isNycDetected props

[28b3413] - 2025-11-05 (v0.9.5)

Added

  • Hyphenated surname support - Individual surname pages now handle hyphenated surnames (e.g., "Bedoni-Kearns") with exact matching and display breakdown data for each surname part
  • Multi-word surname support - Proper handling of surnames like "St Germaine" with variant matching (with/without periods, spaces vs hyphens)
  • BMPM sound-alike suggestions - No-results surname pages now suggest phonetically similar surnames using Beider-Morse Phonetic Matching
  • Progress indicators - Unique surnames section now shows letter-by-letter loading progress (X/26 letters)
  • Counties without unique surnames card - Added informational card listing NY counties that don't have unique surnames
  • Surname URL normalization middleware - Automatically redirects malformed surname URLs (spaces, periods) to canonical hyphenated format

Changed

  • Surnames index page tabs - Fixed Nuxt UI v4 compatibility (now uses value property and #content slot pattern)
  • Surname display count - Changed all time period tabs to show 60 surnames consistently (previously 54 for "all years")
  • Given names fallback logic - Multi-tier threshold: show 3+ occurrences, if <5 results add 2+ occurrences, respects 30-name limit
  • Cities minimum count - Lowered from 2 to 1 to show location data for rare surnames
  • Map zoom for rare surnames - Limited to zoom level 10 (city-level) when 1-3 unique locations to avoid building-level view
  • Surname link generation - All surname links now normalize spaces and periods to hyphens for consistent URLs
  • Unique surnames loading - Reduced stagger time from 5 seconds to 1 second

Fixed

  • St. Lawrence County double-period bug - Added negative lookahead to prevent "St" → "St.." when period already exists
  • Surname tabs data loading - Fixed initial tab not loading and subsequent tabs not triggering data fetch on click
  • CSS scoping issues - Surname page CSS classes now properly apply (space vs no-space in selectors)
  • Hyphenated surname capitalization - "bedoni-kearns" now displays as "Bedoni-Kearns", "st-germaine" as "St Germaine"
  • Hyphenated surname queries - All 5 Solr queries now use record_surname (singular) for exact matching instead of tokenized record_surnames (plural)
  • Multi-word surname variants - Queries now search all combinations: with/without periods, hyphens/spaces (e.g., finds both "ST GERMAINE" and "ST. GERMAINE")
  • Record count pluralization - "1 record found" vs "X records found" on surname pages
  • Font size consistency - Links in "Oldest Recorded Person" section now match surrounding text size
  • H4 heading styling - Added flex layout for icons in surname stat subheadings

Removed

  • Cultural & Immigration Patterns section - Removed from surnames overview page along with component, composable, config, API endpoint, and CSS

Technical

  • Updated surname API to use record_surname (singular field) instead of record_surnames (plural) for exact hyphenated surname matching
  • Added variant query generation for St/St. and space/hyphen combinations in surname slugs
  • Extracted inline Tailwind classes to semantic CSS classes in surname.css (hyphenated parts, similar suggestions)
  • New types: HyphenatedSurnamePart interface for surname breakdown data
  • CSS improvements: consistent gap spacing, proper flex alignment, responsive column spanning
  • Auto-fixed 9 ESLint errors (indentation, arrow functions, const vs let, mustache spacing)

[6723d55] - 2025-11-03

Added

  • Surnames overview page - Complete /people/surnames system with progressive data loading, cultural patterns, and geographic analysis
  • Four surname components - SurnamesMostCommon (with period tabs), SurnamesConcentrated (geographic tags), SurnamesUnique (by county), SurnamesCultural (immigration patterns)
  • Three surname composables - useSurnamesConcentrated, useSurnamesUnique, useSurnamesCultural for progressive alphabet-based loading
  • Seven cultural surname groups - Colonial English, Dutch, Italian, Jewish, Irish, German, and Polish immigration patterns with 30-40+ example surnames each
  • Navigation - "Surnames" added to header menu and footer

Changed

  • Dev server memory - Increased from 8GB to 16GB with additional flags (--max-semi-space-size=512, --expose-gc) for handling large Solr responses
  • Cultural surnames API - Optimized from 70 individual queries to 1 JSON facet query (98.6% reduction)
  • Progressive loading architecture - Staggered timeline (T=0s, T=5s, T=10s) with max 2 concurrent requests, 200ms delays between batches
  • Surname filtering - All APIs now filter out UNKNOWN and surnames with question marks (data quality)
  • Site title - Enhanced with date range and attribution in page titles

Technical

  • New API endpoints: surnames-common, surnames-concentrated-by-letter, surnames-unique-by-county, surnames-cultural-clusters
  • JSON facets used throughout for server-side computation (more efficient than pivot facets)
  • Alphabet-batched loading (A-Z, 2 letters at a time) for concentrated and unique surnames
  • Time period tabs for viewing surname evolution (1880-1920, 1920-1950, 1950-1980, 1980-2017)
  • Result counts always divisible by 6 for clean grid layouts (54 surnames = 9 rows × 6 columns)
  • Error handlers added to all Solr API endpoints (states-and-territories, person-given-name)
  • SurnameStats type moved to types/surname.ts
  • Counties loaded from data file instead of hardcoded arrays
  • Threshold: 100-10,000 records, ≥70% concentration for geographic clustering

[f142c48] - 2025-10-31 (v0.9)

Fixed

  • CRITICAL: Disambiguated place data integrity - All statistics APIs now properly filter by BOTH place name AND county when both are provided, ensuring data from duplicate locality names (Fulton, Albion, Brighton, Chester, Clinton, etc.) is correctly separated instead of mixed together

Added

  • PlaceDisambiguation component - Extracted disambiguation UI from main place page for cleaner code organization
  • usePageTitles composable - Centralized all page title generation logic across person, place, state, and disambiguation pages

Changed

  • Page titles - All place page titles now use consistent format without redundant site name repetition
  • NYC location titles - Changed from "Allerton, Bronx (the Bronx)" to "Allerton, The Bronx, New York City, New York" for clarity
  • Site title - Enhanced with date range and attribution: "The New York State Death Index (1880-2017) - a FREE database from Reclaim The Records"
  • Statistics API behavior - All place statistics APIs (surnames, age, residents-who-left, oldest-resident) now defensively pass county parameter for data integrity

Technical

  • Updated 5 backend APIs with three-path filtering logic: county-only, place-only, or both (disambiguated)
  • Updated 3 frontend components to always pass county when available (not just for known duplicates)
  • Disabled automatic title separator in Nuxt site config (manually control separator formatting)
  • Auto-fixed 131 indentation errors across API files

[ac8b0d2] - 2025-10-31

Added

  • Ambiguous state/town names handling - Support for 11 states whose names match NY locations (Alabama, Delaware, Florida, Maine, Maryland, Michigan, Ohio, Oregon, Texas, Vermont, Washington, Wyoming)
  • PersonInfoBoxes component - Consolidated all person page info boxes (NYC deaths, out-of-state, missing names, truncated names, ambiguous locations) into reusable component
  • StatesExplanation component - Reusable explanation about out-of-state deaths and ambiguous location names for state pages
  • Missing place of death narrative - Explanation when residence is recorded but place of death is not (~4,400 records in 1969, suggests possible out-of-state or NYC death)
  • Same location map handling - Single marker with combined "Place of Death and Place of Residence" label when both locations are identical
  • Complete decade display - Age statistics now show all decades from 1940s onward with "Insufficient data" for decades with <40 deaths
  • County residents metric - Hero section on county pages shows count of residents who died in different NY counties (1957-2017)
  • State page links - Person narratives now link out-of-state deaths to state detail pages
  • Rich tooltips on state cards - UTooltip components with specific notes about ambiguous state names (instant appearance, max-width styling)

Changed

  • Similar records location fallback - Uses residence location/county when place of death is unavailable
  • Truncated name warning - No longer flashes briefly for names that aren't actually truncated (waits for API validation)
  • Map loading placeholders - Person page and search heatmap now use proper-sized placeholders to prevent layout shift
  • Age by decade threshold - Lowered from 50 to 40 deaths minimum per decade for more complete statistics
  • State names in tables - All uppercase display for consistency on state death tables
  • Out-of-state detection - Client-side fallback checks ambiguous state config if Solr field not yet updated
  • PersonNarrative disambiguation - Separate handling for ambiguous state names vs. regular duplicate town names

Fixed

  • Duplicate map IDs - Removed duplicate id="person-map" by moving wrapper to parent page
  • Type organization - Moved OutOfStateStats to proper location in /types/place.ts, removed unused fields
  • Indentation - Fixed 36 indentation errors in states API endpoint

Technical

  • New ambiguousStateNamesConfig.ts with state/town name mappings and explanatory notes
  • Updated placeNarrativesConfig.ts with warnings for Michigan Corners, Texas, and Vermontville
  • Cleaned up OutOfStateMetadata type - removed unused residence fields (pre-1957 data has no overlap with 1957+ residence data)
  • Enhanced Nuxt config with extractAsyncDataHandlers feature and improved Sentry tunnel CORS handling
  • Dependency updates: @sentry/nuxt 10.22.0, Nuxt 4.2.0, ESLint 9.39.0, @antfu/eslint-config 6.2.0

[8f807db] - 2025-10-24

Fixed

  • Linting errors: indentation, button types, JSDoc params, unused imports

[f5f8c06] - 2025-10-24 (v0.8.0)

Added

  • New Solr field integration - Support for location disambiguation, NYC detection, out-of-state detection, and multiple location matches
  • Out-of-state death pages - Complete /places/states/ system with overview, individual state pages, charts, and filterable tables
  • Dual-dataset charts - All location charts now show orange bars/lines for deaths plus blue overlay line for residents who died elsewhere (1957+)
  • US state border overlays - All maps now show orange state borders with smart highlighting for out-of-state deaths (orange fill for target state, blue for NY)
  • Government facility indicators - Blue building icons on county town lists, centralized facility keyword detection
  • Enhanced place narratives - Counties and towns mention residents who died elsewhere and facilities within towns
  • Hash-free navigation - Person page jump buttons scroll smoothly without adding URL hashes
  • Smarter truncation warnings - Hide 8-letter name warnings when top suggestion matches original (e.g., "THEODORE")

Changed

  • Chart legends - Consistent orange/blue colors with ring-style circles, location-specific labels, clickable with pointer cursor
  • NYC county headings - "Towns, Neighborhoods, and Other Locations" for the 5 boroughs instead of "Towns & Cities"
  • Government facility text - "is (or was)" wording for facilities that may no longer exist
  • State pages - Extracted StatesDeathTable component, improved CSS organization with states.css
  • Navigation - Out-of-state deaths added to mega menu and footer with rebalanced columns

Technical

  • Centralized governmentRunFacilitiesConfig.ts for facility keyword detection across codebase
  • New useScrollTo() composable for hash-free smooth scrolling
  • Chart component enhanced with secondaryData, customLabel, and customTooltipTitle props
  • Person page charts now fetch place metadata for year ranges
  • Dev server memory increased to 8GB with Vue deduplication and HMR optimizations
  • Multiple new API endpoints: residents-who-left-by-year, facilities-within, individual state pages
  • US states GeoJSON (87KB) now hosted locally at /public/data/us-states.geojson

[29e98dd] - 2025-10-19 (v0.7.0)

Added

  • Lawsuit page - Complete /lawsuit page documenting the multi-year FOIL case with detailed narrative, timeline, and all legal documents
  • Legal component - Home page section explaining how we won the data through legal action, with featured documents and quotes
  • Timeline visualization - Custom timeline component showing lawsuit progression with orange dots, connecting lines, and document cards
  • Legal document metadata - 9 documents with actual quotes extracted from PDFs, proper author attribution, and color-coded tags
  • Lawsuit types and config - New lawsuit.ts types and lawsuitConfig.ts with centralized document data
  • Download functionality - Working download buttons for all PDF documents
  • Navigation links - "The Lawsuit" added to header menu and footer with links to specific documents

Changed

  • Dev server performance - Disabled ESLint checker, devtools, and view transitions in dev for faster HMR
  • Image optimization - All lawsuit document images use NuxtImg with width/height/quality attributes and AVIF/WebP/JPG/PNG fallbacks
  • Attorney attribution - Michael Moritz properly credited as the attorney who handled most of the case
  • Footer links - Updated with real navigation to lawsuit, search, counties, and RTR resources

Technical

  • Created LawsuitTimeline.vue and LawsuitResources.vue components for modular organization
  • CSS organized in legal.css with lawsuit page styles, timeline styling, and resource cards
  • Document type tags with color-coded badges (amber, red, orange, green, blue, purple, indigo)
  • Real quotes from actual legal filings (FOIL request, denials, briefs, affidavits, amici, court decision)
  • Amici briefs properly credited: Debra Braverman & Roger D. Joslyn (genealogists), The Justice Committee (Gideon Orion Oliver)
  • Vite HMR optimizations: disabled overlay, improved file watching, disabled CSS sourcemaps in dev
  • Memory increased for dev script (4GB) to match build

[c65211f] - 2025-10-19

Fixed

  • API middleware - Allow internal server-to-server requests (fixes charts, towns, and icon loading)

Technical

  • Middleware distinguishes internal requests (no Origin header) from browser requests

[2088f95] - 2025-10-19

Fixed

  • Place data consistency - Explicitly set isResidenceOnly=false for regular places to prevent rendering issues

[3e10d44] - 2025-10-19

Added

  • Residence-only place pages - Places like Allerton (NYC neighborhoods) that only appear in residence data now show properly with fallback queries
  • NYC borough data - Centralized NYC county/borough mappings in /app/data/nycBoroughs.ts
  • NYC-specific narratives - Borough names, 1898 consolidation explanations, and "New York City" context

Changed

  • Page title separator - All page titles now use // instead of - for consistency
  • Quick search CSS - Extracted and namespaced all inline styles to search.css with quick-search-* classes
  • County lists - Deduplicated hardcoded lists in SearchCountySelect and SearchResidence to use counties.json
  • NYC place titles - Include borough names: "Allerton, Bronx (the Bronx), New York"
  • Map popups - Auto-open for NYC locations, residence-only places, and county center fallbacks
  • Map zoom - NYC locations use closer zoom (10 vs 6) for neighborhood context

Fixed

  • Accessibility - Added aria-label to header logo link

Technical

  • Removed orphaned SearchQuickSection component
  • Residence-only places query with same filters as PlaceResidentsWhoLeft for consistent counts
  • Blue markers for residence locations, orange for death locations
  • County center fallback when specific place coordinates unavailable

[b3f0814] - 2025-10-19

Added

  • API host authorization - Middleware blocks all API access from unauthorized domains
  • Security tracking - Unauthorized access, invalid methods, Solr errors tracked in Sentry
  • Server-side email validation - Newsletter signups validated before processing
  • Enhanced CSP headers - Comprehensive Content Security Policy with script/style/font/connect restrictions

Changed

  • CORS handling - Moved from route rules to middleware for dynamic origin checking with wildcard subdomain support
  • Changelog page - Now uses SWR (15-minute revalidation) for auto-updates without full deploys

Technical

  • Middleware checks request host against AUTHORIZED_HOSTS environment variable
  • CORS supports *.newyorkdeathindex.com for beta/staging subdomains
  • Security events logged to Sentry: 400/401/403/404/429/500/503 from Solr, invalid emails, method violations
  • CSP prevents clickjacking with frame-ancestors: none and enforces HTTPS upgrades
  • Build memory increased to 4GB for Sentry source map processing

[d2325fe] - 2025-10-19 (v0.6.0)

Added

  • Sentry error monitoring - Comprehensive error tracking with @sentry/nuxt for production debugging
  • Sentry tunnel - Custom /api/sentry-tunnel endpoint to bypass ad-blockers
  • User identity tracking - Newsletter signups persist user email/name in localStorage for cross-session tracking
  • Search analytics - Full search parameter tracking with natural language vs advanced search differentiation
  • Session metrics - Track total searches, page views, features used, and session duration
  • API performance monitoring - Automatic tracking of slow endpoints (>2s) and failed requests
  • Map issue tracking - Track geocoding failures, missing coordinates, and rendering errors
  • Chart analytics - Monitor chart rendering performance and county comparison feature usage
  • Newsletter analytics - Track signup success/failure and past issues loading performance

Fixed

  • Vue hydration mismatch - Removed duplicate h1 wrapper in person page hero section
  • Request size limits - Increased nuxt-security limits to 5MB for Sentry replay payloads

Changed

  • Vue compiler options - Moved to build-time configuration for Sentry compatibility
  • Person page heading - UPageHero now controls h1 element directly

Technical

  • Session-level metrics persist across component lifecycle (searches, views, features used)
  • Comprehensive tagging: search source/complexity/results, location data quality, API performance, user type
  • Breadcrumbs track user journey: searches → person views → map loads → newsletter signups
  • Context data captures full search parameters, location suggestions, API errors, chart details
  • User identity restoration via client-side plugin on app initialization
  • Sentry tunnel handles compressed responses and 429 rate limits gracefully

[05ed6f5] - 2025-10-13 (v0.5.0)

Added

  • Natural language search - Home page now features intelligent natural language query parsing (e.g., "John Smith from Buffalo who died in the early 1930s")
  • Geographic "near" search - Queries with "near" trigger 10-mile radius search with distance-based sorting
  • Specific date parsing - Supports exact dates like "June 8, 1926" in natural language queries
  • Quick search results - Home page displays top 10 results inline before navigating to advanced search
  • Theme-aware maps - Maps automatically switch between Voyager (light) and Dark Matter (dark) based on color mode
  • SearchQuickResults component - New reusable component for displaying compact search previews

Fixed

  • Map centering - Person location maps now properly center on markers with generous padding for popups, no more shifting when popup opens
  • Map autopan disabled - Eliminated jarring map movement by pre-calculating bounds instead of using Leaflet's autopan
  • Coordinates API for duplicate towns - Now returns most popular location when multiple places share a name (e.g., Monticello in Sullivan vs Otsego)
  • Geographic search sorting - Results now sort by distance first, then name relevance (geodist() function)
  • Location boost increased - Place name boosts increased from ^10 to ^50 for better result ranking
  • County pattern detection - "from Erie County" now correctly extracts county filter instead of place filter

Changed

  • Map tiles - Switched from CartoDB Positron to Voyager for significantly more town/city labels
  • Map zoom levels - Adjusted person maps to zoom 6-7 to show more NY state context
  • Dev script - Now automatically clears .cache/nitro on each dev server start

Technical

  • Natural language parser handles: names (first/last/surname), places (from/in/near), counties, dates (specific/decades/years), ages, time modifiers (before/after/early/late)
  • Preprocessing removes filler phrases: "anyone who", "people that", ", NY", state name variations
  • Pattern matching rejects location keywords as names (in, from, near, died, etc.)
  • Smart decade word parsing: "eighties", "1980s", "80s" all normalize to 1980-1989
  • SessionStorage for passing parameters to advanced search (no URL query strings)
  • Explicit date support with MM/DD/YYYY format for exact date searches

[765d981] - 2025-10-12 (v0.4.0)

Added

  • Enterprise-grade caching system - Refactored all 16 API endpoints from manual useStorage to defineCachedEventHandler with 30-day caching, filesystem storage, and SWR (Stale While Revalidate)
  • Mass prerendering - 162 pages now prerendered in production (4 static + 62 counties + 96 top localities) for instant page loads
  • Top localities data - Created /app/data/topLocalities.json with 96 most popular towns/cities (excluding government facilities) for prerendering
  • Changelog page - New /about/changelog page displays CHANGELOG.md with @nuxtjs/mdc, linked from construction banner
  • Organized cache structure - API caches now stored in PascalCase folders (FacetByYear/, OldestResident/, etc.) with normalized filenames
  • Build datetime display - Construction banner shows last deploy time in Pacific timezone
  • Auto-clearing search cache - Client-side search results auto-clear on deploy via build datetime check, with 2-hour expiration
  • Smart sitemap - Auto-discovers ~162 prerendered pages, excludes 10M+ person pages
  • Custom router scroll behavior - Pages load at top instantly (no dizzying scroll animation from bottom)
  • View transitions - Experimental Nuxt 4 view transitions for smooth page-to-page animations (person names, heroes, content)
  • Map/chart placeholders - Reserved height prevents layout shift when ClientOnly components load

Fixed

  • FOUC (Flash of Unstyled Content) - Enabled inlineStyles: true to inline critical CSS
  • FOUT (Flash of Unstyled Text) - Explicit font family declarations for Young Serif, Roboto, and Instrument Sans
  • Layout flashing - Removed lazy: true from all page data fetching for immediate server-side rendering
  • Map layout shifts - Added min-height placeholders for all maps and charts (380px-600px depending on type)
  • Banner flashing - Pre-format build date server-side to avoid client-side Day.js processing
  • Header logo flashing - Added ClientOnly wrapper with SSR fallback for color mode logo switching
  • Counties page progressive loading - Grid cards now visible immediately with "Loading..." text that fills in as API calls complete (batches of 5)
  • Towns section loading state - Shows "Loading towns..." instead of "No results" during initial fetch

Changed

  • Caching storage - Switched from memory to filesystem (.cache/nitro) for serverless-friendly operation
  • Cache durations - Extended from 7 days to 30 days for all API endpoints (data changes monthly at most)
  • Route cache - Increased from ~10 minutes to 30 days for /people/** and /places/** routes
  • CDN cache headers - Added 30-day cache-control headers for all /api/* routes
  • Routing structure - Moved about.vue to about/index.vue to enable /about/changelog sub-route
  • Font loading - Moved from implicit to explicit font families with weight specifications

Technical

  • All cached API endpoints now use explicit H3Event typing for serverless/edge compatibility
  • Cache keys use PascalCase folder names and normalized filenames (lowercase, hyphenated, periods removed)
  • Centralized all animations and view transitions in main.css with clear documentation
  • Created router.options.ts for custom scroll behavior configuration
  • Added @nuxtjs/mdc module for markdown rendering
  • Build datetime in runtimeConfig.public for automatic cache invalidation (no manual version bumps!)
  • Prerendering only runs in production (fast dev server startup)
  • Smart duplicate locality handling in prerender (e.g., Rochester → monroe-county specific slug)

[1479e46] - 2025-10-12 (v0.3.0)

Added

  • Newsletter integration - Full Mailchimp integration with signup form, past issues list, and server-side caching
  • Truncated given name detection - Smart detection and suggestions for 8-letter given names (1951-1978 records) using Solr facet prefix matching
  • Out-of-state death handling - Automatic detection of deaths in other U.S. states/territories with state-centered maps and appropriate messaging
  • States statistics page - Overview page at /places/states showing all non-NY deaths with counts and year ranges
  • Newsletter CSS - Responsive three-column layout with custom form styling and scrollable past issues
  • State name detector utility - Validates state/territory names and provides center coordinates for mapping
  • Centralized slug utilities - slugPerson.ts and slugLocality.ts for consistent URL generation across entire codebase
  • Enhanced place fuzzy matching - Levenshtein distance calculation, wildcard contains search for multi-word truncated places
  • API suggestions folder - Organized all suggestion endpoints in /api/suggestions/ (place-autocomplete, place-fuzzy-match, person-given-name)
  • Stats API folder - New /api/stats/ for aggregate database analysis endpoints
  • Newsletter types - Complete type definitions in types/newsletter.ts
  • Search types - New types/search.ts for search-related interfaces
  • Chart legend sizing - Configurable font sizes for both canvas and HTML legends

Fixed

  • Authorization bug in getAuthorizedHosts - Fixed undefined vs null check that was causing 500 errors
  • Person slug generation - Now correctly uses actual death year instead of parsing from record_id
  • Checkbox styling - Removed blue browser defaults, all form elements now use consistent orange theme
  • Map county boundary slugs - Now uses centralized generateCountySlug() utility
  • Newsletter caching - Simplified client-side cache logic, relies on robust 24-hour server-side Nitro cache
  • Search endpoint imports - Fixed relative paths after moving from /api/people/search to /api/search
  • 1951-1956 vs 1957-1978 pattern - Different validation for middle initials in given name field vs separate field

Changed

  • Renamed "Related Records" to "Similar Records" - Throughout codebase (components, composables, CSS classes, user-facing text)
  • Main search endpoint - Moved from /api/people/search to /api/search (cleaner top-level endpoint)
  • Type organization - Extracted all inline types from components to centralized type files
  • recordId → slug property - Renamed in all API responses and types for clarity
  • Place fuzzy matching - Enhanced with normalized edit distance, prefix matching for truncated multi-word places, and flexible confidence thresholds

Technical

  • Created comprehensive type files: newsletter.ts, search.ts (70+ inline types extracted)
  • Renamed files: geoSlugs.ts → slugLocality.ts, useRelatedRecords.ts → useSimilarRecords.ts, PersonRelatedRecords.vue → PersonSimilarRecords.vue
  • All CSS class names updated from person-related-* to person-similar-*
  • Newsletter section uses defineCachedEventHandler with 24-hour cache
  • State coordinates added for all 50 states, DC, and territories in statesAndTerritories.ts
  • All suggestion endpoints now have consistent 30-day caching

[42f5484] - 2025-10-10 (v0.2.0)

Added

  • Place name correction system - Fuzzy matching with Levenshtein distance suggests correct town names for misspellings (e.g., "OTICA" → "Utica")
  • NYC certificate detection - Recognizes NYC death certificates (e.g., "M10871") and provides NYC-specific ordering instructions
  • Place name variants system - Combines data from multiple spellings of same place ("Village of the Branch", "Clifton Park" vs "CliftonPark")
  • Government facility notices - Info boxes explain privacy approach for hospitals, prisons, etc.
  • County Wikipedia links - All 62 NY counties now have Wikipedia links in data file
  • Clickable county boundary maps - Click any county on the SVG map to navigate to that county page
  • Pre-1915 city warnings - Albany, Buffalo, Yonkers pages explain missing early records
  • Multiple oldest people display - Shows all people who died at the same maximum age (fairness!)
  • 1972 record handling - Explains missing death location field, uses residence data as fallback
  • Consolidated county data - Single counties.json file with names, coordinates, and Wikipedia URLs
  • Types organization - Centralized types in /types/ (place.ts, surname.ts, api.ts)
  • Solr client utilities - Reusable auth and parsing functions (reduced 420+ lines of duplication)

Fixed

  • Residence-only maps - 1972 records and others without death location now show residence marker
  • Similar records fallback - Uses residence location when death location unavailable
  • Suggested locations flow through entire page - Maps, charts, and certificate ordering all use corrected place names
  • Place variants combined everywhere - Charts, stats, search results all combine variant spellings
  • Special name capitalization - DeRuyter, McConnellsville, Clifton Park, V.A. facilities all work correctly
  • Single-death localities - Better grammar ("one death...in 1903" vs "1 death...from 1903 to 1903")
  • Pivot facets for duplicate localities - Surname pages, autocomplete, all APIs properly separate duplicate town names
  • Related surname search - Now expands radius to get 20 results (was stopping at 5)

Changed

  • Place corrections server-side - No more client-side pop-in, entire page loads with corrected data
  • API code consolidation - 14 API files refactored to use centralized Solr helpers

[c81f36b] - 2025-10-08

Added

  • Duplicate locality disambiguation system for 13 town/city pairs (Fulton, Rochester, Albion, etc.)
  • Disambiguation picker pages with clickable cards for ambiguous localities
  • Place autocomplete now shows county names to distinguish duplicates
  • Government facilities filtered from autocomplete for privacy
  • Most common given names on surname pages (normalized, filtered)

Fixed

  • All place links pass county data for proper disambiguation
  • Autocomplete separates duplicate localities instead of combining them

Changed

  • Renamed "towns" to "localities" throughout codebase

[87d762a] - 2025-10-08

Added

  • Surname pages with heatmap, statistics, and top locations
  • NYS DOH comments on locality pages
  • PlaceNotice on person pages
  • Lowercase URL canonicalization middleware
  • Support for place name abbreviations (St., Dev., Mem., V.A., etc.)

Fixed

  • Chart background shading reaches chart top
  • Person links use full SEO-friendly slugs
  • St. Lawrence double period bug
  • Given name case normalization

[765a542] - 2025-10-08

Added

  • OG Image templates with brand styling
  • Base64-encoded RTR logo for OG images

Changed

  • Updated dependencies

[02c2bf4] - 2025-10-06 (v0.1.0 - Alpha Release)

Added

  • Initial public alpha release of the New York State Death Index website
  • Advanced search functionality with Solr backend
  • Person detail pages with location maps and related records
  • Place/county pages with statistics, charts, and demographics
  • Interactive charts showing deaths by year with special historical period highlighting
  • County comparison charts with customizable selections
  • Mega menu navigation with all 62 NY counties
  • Dynamic chart theming for special years (Spanish Flu, data gaps, NYC consolidation, pre-1915 cities)
  • Project attribution component featuring RTR and NYS DOH logos
  • Sticky navbar with blur effect and responsive mega menu
  • County-specific and locality-specific narrative text system
  • Smart auto-scroll on search (only when user submits, not on navigation)
  • Hamlet/small town detection with "within town" references
  • ResizeObserver-based dynamic height syncing for county comparison chart
  • Comprehensive CSS organization (navbar.css, components.css, place.css, maps.css, etc.)
  • Responsive design with mobile hamburger menu
  • Dark mode support throughout with logo switching
  • Chart configuration system separating editable config from utilities

Technical

  • Built with Nuxt 4, TypeScript, and Tailwind CSS
  • Nuxt UI v4 components for consistent design
  • Chart.js for data visualization
  • Leaflet maps for geographic visualization
  • Solr search backend with phonetic matching
  • IndexedDB caching for search results
  • Server-side rendering with client-side hydration

Sign up for our free newsletter

Want to learn more about Reclaim The Records and our ongoing work to obtain and publish genealogical and archival data through Freedom of Information requests and litigation? Sign up for our mailing list! We'll let you know about newly-released free public data, but we won't spam you or sell your address.