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.tslogs 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
cachePersonandcacheSurnamestorage mounts innuxt.config.tsusing Unstorage'slruCachedriver with automatic eviction of least-used entries
Fixed
- Memory management plugin async/await - Fixed TypeScript error where
logMemoryUsage()usedawaitwithout 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-promisesrule - 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.tsproactively 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.mdexplaining 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.tsto prevent multiple wrapping ofglobalThis.$fetchand reduce closure scope to minimize memory retention
Changed
- Production memory allocation - Increased Node.js heap limit from ~1GB to 2GB via
NODE_OPTIONSin 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.tsexplaining filesystem cache driver limitations (keys still tracked in memory) - API auth plugin now uses
globalThisinstead ofglobalper 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
cachetocacheFileSystemwith 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 CSPimg-srcfor 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.tsadds 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.tsmiddleware to useSec-Fetch-Siteheader for validating browser requests, block direct URL access to API routes, and require internal auth secret for SSR requests - Name sanitization whitelist - Changed
nameSanitizer.tsfrom 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
useAsyncDatatouseLazyAsyncDataso 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
apiInternalSecretto 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.mdwith full GPLv3 license text - Global error handler plugin - New
server/plugins/error-handler.tscatches 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
ignoreErrorsconfig to filter out benign errors: View Transition aborts, ResizeObserver loops, network failures, browser extension errors
Changed
- Replaced all
console.errorwith structured logging - All 49 server-sideconsole.errorcalls now use appropriate Pino loggers (logSolrError,logExternalApiError,logWarning, etc.) for better log aggregation and filtering - Removed misleading
fromCacheproperty - Handler-level cache detection wasn't working with filesystem caching; removed from all API handlers and logger interfaces - Cache detection via timing - Added
cacheStatusfield 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: falseto 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
/legalpath (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
crawlLinksfor prerendering to speed up builds, addedfailOnError: false, configured Young Serif and Roboto fonts for OG images - All pages now use explicit imports for OG image composables
- Removed unused
ogStatscomputed 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
PlaceorAdministrativeAreaJSON-LD schema with geo coordinates, containment hierarchy, and optional Wikipedia links - Site-wide Schema.org - Default layout includes
WebSiteschema withspatialCoverageset 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] Smithor[Male] Joneswhen 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.tsfor places with ampersands, parentheses, or unusual formatting (e.g., "NYS Home for Vet & Depen", "Mohawk Valley (Utica) Psy Ctr") - Bluesky types - New
bluesky.tstype definitions for feed components and API responses - Cloudflare cache purge script - New
scripts/purge-cloudflare-cache.shruns 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 (
flparameter) 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_surnameANDrecord_surnamesfields (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-gridwith 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/nuxt10.29.0,@antfu/eslint-config6.4.1,eslint9.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;
usePageTitlescomposable now returns only page-specific part,@nuxtjs/seotitleTemplate 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 CSPframe-srcfor 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
isBottoisBotUserAgentto 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
nullto 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-dividerfrom chart and towns sections to prevent stacked borders
Technical
- Bluesky API integration:
/server/api/bluesky/feed.tsendpoint 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) andrecord_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.csswith scoped Tailwind-based styles for the duplicates analysis page - Duplicates API endpoint - New
/api/people/duplicatesendpoint 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.jsonfor all 62 NY counties useEffectiveLocationscomposable - New reusable composable for determining effective death/residence locations (handles geocoded, NYC, suggested, and problematic codes)inferredLocationDisplayutility - Lightweight utility for search results to detect and display inferred locationsproblematicLocationCodesutility - 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_problematicfield 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,InferredLocationDisplayinterfaces - 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, andisNycDetectedprops
[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
valueproperty and#contentslot 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 tokenizedrecord_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 ofrecord_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:
HyphenatedSurnamePartinterface 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/surnamessystem 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
OutOfStateStatsto proper location in/types/place.ts, removed unused fields - Indentation - Fixed 36 indentation errors in states API endpoint
Technical
- New
ambiguousStateNamesConfig.tswith state/town name mappings and explanatory notes - Updated
placeNarrativesConfig.tswith warnings for Michigan Corners, Texas, and Vermontville - Cleaned up
OutOfStateMetadatatype - removed unused residence fields (pre-1957 data has no overlap with 1957+ residence data) - Enhanced Nuxt config with
extractAsyncDataHandlersfeature 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.tsfor facility keyword detection across codebase - New
useScrollTo()composable for hash-free smooth scrolling - Chart component enhanced with
secondaryData,customLabel, andcustomTooltipTitleprops - 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
/lawsuitpage 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.tstypes andlawsuitConfig.tswith 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.vueandLawsuitResources.vuecomponents for modular organization - CSS organized in
legal.csswith 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.csswithquick-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.comfor 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: noneand 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-tunnelendpoint 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/nitroon 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
useStoragetodefineCachedEventHandlerwith 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.jsonwith 96 most popular towns/cities (excluding government facilities) for prerendering - Changelog page - New
/about/changelogpage 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: trueto inline critical CSS - FOUT (Flash of Unstyled Text) - Explicit font family declarations for Young Serif, Roboto, and Instrument Sans
- Layout flashing - Removed
lazy: truefrom 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-controlheaders for all/api/*routes - Routing structure - Moved
about.vuetoabout/index.vueto enable/about/changelogsub-route - Font loading - Moved from implicit to explicit font families with weight specifications
Technical
- All cached API endpoints now use explicit
H3Eventtyping 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.tsfor custom scroll behavior configuration - Added
@nuxtjs/mdcmodule for markdown rendering - Build datetime in
runtimeConfig.publicfor 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/statesshowing 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.tsandslugLocality.tsfor 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.tsfor 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/searchto/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/searchto/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-*toperson-similar-* - Newsletter section uses
defineCachedEventHandlerwith 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