RESO Tools – Release Notes
v0.10 – “Ten Ichi”
XSD and Semantic Metadata Validation
Added OData CSDL metadata validation to all certification pipelines (DD, Core, Add/Edit, EntityEvent). Metadata is validated after fetch, before any endorsement-specific testing.
XSD Structural Validation
- Validates XML metadata against the official OASIS OData EDM + EDMX schemas (4.0 and 4.01)
- Uses libxml2-wasm (MIT, zero dependencies, WASM-based libxml2) for cross-platform compatibility — works identically in Node.js CLI and Electron desktop client with no native binary ABI issues
Semantic CSDL Validation
- Nine checks ported from Apache Olingo’s CsdlTypeValidator: entity type keys, base type resolution, entity set references, navigation binding paths/targets, nav property type matching, referential constraints
- Each error includes a link to the relevant section of the OASIS OData 4.0 or 4.01 CSDL specification
- Commander test fixtures (16 files) included for regression parity
Dependency: libxml2-wasm replaces libxmljs2
| libxmljs2 (old) | libxml2-wasm (new) | |
|---|---|---|
| License | MIT | MIT |
| Dependencies | 25+ (native build chain) | 0 |
| Vulnerabilities | 0 | 0 |
| Package size | ~15MB (with native build) | 1.4MB |
| Native binary | Yes (requires ABI match) | No (WASM, cross-platform) |
| Electron compatible | Requires electron-rebuild | Works out of the box |
Why: libxmljs2’s native binary must match the Node.js ABI version. Electron uses a different ABI than system Node, causing version mismatch errors. libxml2-wasm uses the same libxml2 engine compiled to WebAssembly, which runs identically in any JavaScript runtime.
Reference Server
- EntityEvent reset fix: data reset now skips read-only resources (EntityEvent, Lookup) so their data is preserved
- Lookup Resource reconciliation: after data generation, the server inserts any enum values used in generated data into the Lookup Resource so they are advertised in the metadata
- Data generator: stopped filtering out placeholder enum values, added RESO enum namespace fallback
- Reference metadata: removed 29 placeholder lookup values from DD 2.0 and 37 from DD 2.1
Desktop Client (v0.10.0)
- Read-only resources show lock icon in nav with “Read-only resource” tooltip
- Detail page hides Edit/Delete buttons for read-only resources
- Dashboard recent jobs are clickable — navigates to Jobs page with highlight and auto-expand
- Fixed cert runner import path for dev mode
- v0.9 binaries rebuilt with correct version in filenames
DD Documentation Site
- Open enumerations now generate proper lookup pages instead of dead links (37 new pages for DD 2.1)
- Lookup index shows “Open” label for enumerations with no standard values
MCP Server Guide
- Added architecture diagram showing the AI trust boundary
- Responsive design, centered, clickable to expand
Transport Site
- Fixed dark mode link colors inside card bodies and policy changes page
Policy
- Added new dependency security audit requirement to CLAUDE.md
Real-Time Replication Progress (#126)
RESO Analytics information during DD replication testing, including real-time progress and industry comparison data.
onProgresscallback added toreplicate()for real-time progress during replicationdata-availability-responses.jsonnow includes astatssummary section- Sub-step progress events now update the UI in real time
Login and Notifications
- Case-sensitive username, apiToken for OAuth2, stale credential cleanup
- Notification bell with services.reso.org polling
- Services API client (notifications, jobs, variations)
Certification Pipeline
- Pipeline refactor: steps support sub-function arrays (sequential/parallel mode)
- Cert runner moved to Worker thread (main process event loop free for IPC)
- DD pipeline: replication strategies as sub-functions with detailed report writing
- Request delay and 429 wait time configurable on DD config
- 3-year lookback (was 2)
UI
- Escape key closes all modals
- Back/forward navigation
- Lock icon for read-only resources
Tickets
- #123 XSD validation
- #124 Reference metadata with annotated empty enumerations
- #125 Monadic test container pattern
- #126 Real-time step progress during DD replication
- reso-certification#2543 Sample certification report fixtures
Package Versions
| Package | Previous | New | Reason |
|---|---|---|---|
| reso-desktop-client | 0.9.0 | 0.10.0 | Cancel job, release name, worker progress |
| reso-certification | 0.8.0 | 0.9.0 | onProgress callback, Welford’s stats, sub-function pipeline |
| reso-web-client | 0.3.0 | 0.4.0 | Progress bar chart, industry baseline, dashboard loading |
Unchanged: reso-client (0.1.0), reso-data-generator (0.2.0), reso-reference-server (0.8.0), reso-mcp-server (0.8.0), reso-validation (0.1.0), odata-expression-parser (0.1.0), reso-web-api-proxy (0.1.0)
Tests
1,237 tests across 8 packages (up from 1,197 in v0.9)
v0.9 – 2026-04-14
To the Nines
End-to-end certification testing from the RESO Desktop Client, plus a cloud MCP server for AI agents.
Download desktop binaries — macOS, Windows and Linux.
Certification Testing
Run Data Dictionary, Web API Core, Add/Edit, and EntityEvent tests directly from the desktop app — no CLI, no Docker required for basic testing.
- Config Builder — RESO org picker from the certification directory, multi-recipient support, per-endorsement option panels (DD, Core, Add/Edit, EntityEvent), concurrency settings, JSON import/export compatible with legacy dd-config format
- Job Manager — real-time step progress, re-run with secure config storage, delete individual or all results, job history persists across app restarts
- Error Reports — DD schema validation errors grouped by error type with expandable fields, scrollable lookup values, copy icons, CSV key download. Core/Add/Edit/EntityEvent scenario failures with humanized names and assertion-level details. Variations with cross-field mapping and multi-suggestion support. Spec links for all endorsement types.
- Compliance Reports — passed job step breakdown with expandable scenario accordions showing individual pass/fail results
- Dashboard — live testing activity overview with org name resolution, expiring endorsements from cert API, passing/failing summary cards
- EntityEvent — enabled on the embedded reference server, observe mode with live polling progress counter
Cloud MCP Server
RESO now hosts a cloud MCP server at services.reso.org/mcp. Any MCP-capable AI agent can query RESO-compliant servers through it with an API key. Contact dev@reso.org to request access.
Available tools: authenticate, query, metadata, create, update, delete, parse-filter.
SDK and Infrastructure
- Consistent nested output paths for all endorsements
throwOnErrorflag on cert-utilsreplicate()for clean Electron error propagation- SHA-256 for Electron BoringSSL compatibility
- Server-driven paging test fix (single page with no nextLink is valid)
- Detailed reports include per-scenario assertions for all endorsement types
- 1,197 tests passing across 8 packages
v0.8 – 2026-04-12
Eight Days a Week
The cert UI ships in the desktop client with three detail views for every Data Dictionary endorsement, a full Org Summary page with live analytics, and a performance report with industry comparison.
Download desktop binaries — macOS, Windows and Linux.
Cert Detail Report
Three views for DD endorsements, accessible via a toggle with icons:
- RESO Analytics — hero tiles with clickable counts, expandable resource cards with per-resource standardization, availability distribution with industry comparison
- Server Explorer — metadata browser powered by the cert API (no live server needed). Data set filters (All/RESO/Local/IDX), element type filters (All/Fields/Enums/Expansions), availability threshold with preset stops at p90/p95/p99, search across fields and lookup values, sort by name/type/availability with direction toggle, four-tab field detail (Lookup Values, Data Dictionary, OData Info, Annotations)
- Performance — provider vs. industry comparison bars for response time, throughput and payload size. Per-resource replication throughput with industry average markers. Per-resource sampling cards with detailed stats
Web API Core and generic renderers for all other endorsement types.
Org Summary Page
Coverage tiles (RESO Fields with Data, RESO Lookups with Data, Field Standardization, Lookup Standardization, Local Fields) with industry comparison. IDX Payload section with per-resource bars. Performance section with headline replication speed, payload, response and throughput metrics with delta callouts. Provider switcher with View Details navigation. Expiring Soon badges with dashed-underline tooltips.
Data Generator
City-specific street names from 75 real U.S. locations. Field-name-aware numeric bounds (~40 rules). Geo-consistent addresses across Property, Member and Office. Co-agents from the same office as primary agent. 20 real MLS system names. Reset button with two-step confirmation.
MCP Guide
Section 4 is now a standalone Add/Edit walkthrough with real server data. Section 5 builds on it with EntityEvent change tracking. All cross-references updated.
Shared Components
Badge, FieldRow, FilterPill, SearchInput, ResourceButton, AvailabilityThresholdPills extracted to a shared module so the live metadata browser and cert Server Explorer stay visually consistent.
Quality
- 1,185 tests passing across 8 packages (up from 1,097 in v0.7)
- Enum detection with 11 dedicated tests
- Performance metrics adapter with 9 tests
- Cert summary adapter and detail report renderer selection tests
v0.7 – 2026-04-06
Return of the MCP
The MCP server picks up where v0.6 left off. v0.6 shipped seven read tools and proved AI agents could drive RESO end to end; v0.7 fills in the gaps that real-world Add/Edit work needs and gives AI integrators a real walkthrough to learn from.
Write tools – create, update, delete
Three new tools wrap the OData POST / PATCH / DELETE paths against any RESO resource. Each carries the standard MCP ToolAnnotations (destructiveHint, idempotentHint, openWorldHint), so compliant hosts like Claude Code, Claude Desktop, and IDE extensions can prompt the user before invoking destructive operations. delete is the only one tagged destructiveHint: true; create and update rely on the assistant-side confirmation dance instead.
The handlers reuse the same odataRequest + buildResourceUrl(serverUrl, resource, key) plumbing the certification runner uses, so behavior is consistent with the rest of the toolchain.
MCP Server User Guide
reso-mcp-server/doc/GUIDE.md is a dialogue-format walkthrough of working with a RESO OData server through an AI assistant. Every tool call and every response in the guide was captured live against the seeded reference server with ENTITY_EVENT=true. Sections cover:
- Authentication – both bearer-token and Client Credentials, framed as a real first-contact conversation
- Metadata exploration – what 14 resources look like on the wire, the field-type breakdown of
Property, the lookup reverse index that powers dd.reso.org - Querying the Lookup resource to resolve allowed values for any lookup-backed string
- Searching – range filters, string matching, multi-value lookup lambdas (
Appliances/any(...)), date windows for incremental sync, andparse-filterfor sanity-checking complex expressions before sending - Add/Edit and EntityEvent – the complete write loop, including the polling-replication consumer pattern (404 or empty result on follow-up fetch means delete; otherwise compare or hash to detect change), the canonical 30-line sync loop, and a dedicated section on error handling that walks through structured 400s, structured 404s, and the silent-200 trap when servers do not enforce lookup membership on PATCH
The guide is intentionally written as user-and-assistant dialogue rather than API reference – every section starts with a real question and ends with the assistant’s interpretation of the live response.
EntityEvent end-to-end through MCP
The reference server’s ENTITY_EVENT=true mode is now confirmed working through the MCP layer with no additional code in the MCP server itself. The generic OData passthrough in the query and metadata tools surfaces the EntityEvent resource (DD 2.1 schema: EntityEventSequence, ResourceName, ResourceRecordKey, ResourceRecordUrl, FeedTypes) automatically once the flag is enabled. Section 5 of the user guide demonstrates the full pull-based replication loop end to end.
Harness coercion fix
The MCP server’s JSON-Schema-to-Zod converter now uses z.coerce.number(), z.coerce.boolean(), and z.preprocess(JSON.parse, ...) for objects and arrays. This means MCP harnesses that serialize all tool arguments as strings (e.g. XML-text parameter forms) no longer trip strict validation on top, skip, count, or structured record payloads. Single-point fix in jsonPropToZod, applies to all 10 tools.
Desktop About panel
The release name in the About panel is now read from a single RELEASE_NAME constant in reso-desktop-client/src/main.ts, and the version is read automatically via app.getVersion() so it can never drift from package.json again. The release checklist in CLAUDE.md documents the new pattern.
tools.reso.org search
The Pagefind search index is now built and deployed for tools.reso.org via the GitHub Pages workflow. The site layout already had the search modal wired up; the workflow was missing the npx pagefind --site step that DD and Transport already have. Search now works on both desktop and mobile.
Tests and quality
- 1,097 tests passing across 8 packages (was 1,096 in v0.6)
- New
tools.test.tscases assert the destructive-hint annotations oncreate/update/deleteso the safety contract is enforced in CI
v0.6 – 2026-04-06
The MCP Strikes Back
The Java-based web-api-commander has been retired. All certification testing now runs natively in TypeScript on Node.js 22+. The MCP server is now built and smoke-tested in CI against the reference server, so AI agents can drive RESO end to end with confidence.
Note on versioning: v0.5 was published briefly with a tag protection issue that prevented re-releasing it after a workflow fix. Rather than fight the protection, we skipped to v0.6 with the same scope plus the release-workflow improvement. v0.5 should be considered superseded.
Certification CLI (reso-cert)
- Add/Edit (RCP-010) – 8 scenarios with auto-generated payloads, listr2 progress, config file support
- EntityEvent (RCP-027) – observe + full mode, 9-12 scenarios
- Web API Core 2.0.0/2.1.0 – 45+ data-driven scenarios, auto-detects enum mode (isflags/collections/string), coverage matrix, per-request latency tracking,
--full-coverageflag - Data Dictionary 2.0/2.1 – full pipeline: metadata serialization, Lookup Resource merge, variations checking, 3 replication strategies (TIMESTAMP_DESC, NEXT_LINK, NEXT_LINK+filter)
- Auth chain – resolves from CLI flags > config file >
.env>RESO_*env vars - Output – listr2 spinners (default),
--verbosefor CI,--output jsonfor piping
MCP Server (reso-mcp-server)
New package – 7 tools: authenticate, query, metadata, validate, parse-filter, run-compliance, metadata-report. Works with Claude, Cursor, VS Code, Windsurf, JetBrains, Zed, and any MCP client.
Metadata Tooling
- EDMX → JSON serializer – replaces Commander’s MetadataReport + FieldJson + LookupJson Java serializers
- XLSX → JSON generator – replaces Commander’s codegen. Reads DD sheets, produces reference metadata
- Lookup Resource fetcher –
@odata.nextLinkpagination with$top/$skipfallback - DD 2.0 and 2.1 reference metadata generated and validated (41→44 resources, 1745→2170 fields, 3611→4177 lookups)
Desktop Client
- DD version chooser – Server menu with DD 2.0 / DD 2.1 (Draft) selection
- Per-version SQLite databases – switching versions does not destroy data
- Version display – window title and server switcher show active DD version
Data Generator
- Lookup resolution fix – uses LookupName annotations for string-based enum lookup
- Collection() type unwrapping –
buildLookupMapnow unwrapsCollection(org.reso.metadata.enums.X)so multi-value enum fields resolve their lookups correctly - Human-friendly values – StandardName annotations produce DD-compliant values
- Placeholder filtering – skips SampleXxxEnumValue placeholders for open enums
- Realistic open enum values – 34 open enum types (City, CountyOrParish, schools, districts, MLS areas, etc.) now have realistic PascalCased lookup values instead of SampleXxxEnumValue placeholders
Docker and CI
- Simplified entrypoints – all compliance containers use one Dockerfile (no more Dockerfile.core, Dockerfile.dd)
- DD strict mode –
entrypoint-dd.shnow passes--strict, failing on variations and schema validation errors instead of silently logging them - MCP smoke test in CI – new
compliance-mcpDocker service builds the MCP server image and runs JSON-RPC requests against the reference server (5/5 tools verified: initialize, tools/list, metadata, query, parse-filter) - Release workflow fix –
gh release createwould fail when a draft release was published manually before the workflow ran. The workflow now checks if a release already exists and uploads to it instead of trying to create a duplicate. - Express wildcard fix – updated
*routes to/{*path}for newer path-to-regexp - CI workflow – added reso-certification and reso-mcp-server to build and test pipeline
Package Naming
- Consistent
@reso-standards/reso-*convention – renamed@reso-standards/reference-server,@reso-standards/web-client,@reso-standards/desktop-client, and@reso-standards/web-api-proxyto include thereso-prefix, matching the folder names and other packages
Documentation
- Per-endorsement READMEs – test matrices, enum modes, coverage, pipeline steps
- Migration guide – command mapping from cert-utils/Commander to reso-cert
- Release workflow added to CLAUDE.md
- DD 2.1 documentation live at dd.reso.org
- LICENSE links – all package READMEs now link to the GitHub LICENSE URL (fixes 404s on GH Pages and npm)
- GH Pages – updated hero stats, test counts, CLI examples, added MCP server to package listing and architecture diagram, fixed mobile nav layout
Data Quality
- Unicode BOM fix – removed invisible U+FEFF (byte order mark) from “Full Exposure” lookup value in DD 2.1 reference metadata and server metadata. Fixed XLSX source sheets for DD 2.0 and 2.1 (21 NBSP and 1 BOM cleaned)
- DD strict compliance – 8/8 DD steps pass with
--strictflag: health check, auth, metadata report, replication state, variations (0 found), and all 3 replication strategies (TIMESTAMP_DESC, NEXT_LINK, NEXT_LINK+filter)
Performance
- Vitest benchmarks – parser (1.1M ops/sec), assertions (18M ops/sec), query building (3.6M ops/sec)
--batch-expandoption for DD testing (batches $expand per resource)- Coverage reporting via @vitest/coverage-v8
Numbers
- 1,096 tests across 8 packages
- Commander retirement: web-api-commander#215
v0.4 – 2026-04-05
Package Restructuring and Desktop Client Improvements
Fourth milestone release restructuring packages for the upcoming RESO compliance testing integration, fixing desktop client startup issues, and adding update checking and theme persistence.
Package Restructuring
- Renamed packages –
validation/→reso-validation/,data-generator/→reso-data-generator/,certification/→reso-certification/with updated package names (@reso-standards/reso-validation,@reso-standards/reso-data-generator,@reso-standards/reso-certification). - Added
reso-certification-utils@3.0.0– RESO certification SDK added as a dependency ofreso-certification, installed from GitHub. Provides DD testing, replication, schema validation, variations detection, and UPI validation. - Updated all cross-references – Imports,
file:dependencies, Dockerfiles, CI workflows, docker-compose, lefthook, and documentation.
Desktop Client
- Splash screen – RESO logo with spinner shown while the server initializes. Adapts to dark/light mode. No more white flash on startup.
- Dark mode persistence – Theme preference saved to Electron secure storage. Survives app restarts (previously lost because
localStorageis per-origin and the server uses random ports). - Update checker – Checks GitHub releases on launch (silent notification badge) and from Help > Check for Updates (interactive dialog). Release URL validated against expected GitHub domain.
- Server bundle fix – Native dependencies (
better-sqlite3) now resolved from the correctnode_modules. Stub packages forpgandmongodbprevent ESM resolution failures in SQLite-only desktop mode. - EPIPE fix – Broken pipe errors on shutdown no longer crash the app.
- Window lifecycle – Window hidden before close to prevent flash.
show: falseuntil content is ready. - Help menu – Added links to Releases, Announcements, and Security Audit on tools.reso.org.
Security
navigateToinjection fix – Menu navigation paths are now JSON-serialized instead of string-interpolated intoexecuteJavaScriptcalls, preventing potential script injection.- Release URL validation – Update checker validates that release URLs point to
https://github.com/RESOStandards/reso-tools/releases/before opening in the browser.
Release Workflow
- Tag-triggered builds – Release workflow now triggers on
v*tag push instead of release events. Builds all three platforms (macOS, Windows, Linux) and creates the GitHub release with binaries attached automatically. - Cross-platform postinstall – Removed macOS-only
PlistBuddycommands frompostinstall. - Linux .deb fix – Added
artifactNameto electron-builder config to avoid invalid paths from scoped package names.
Site
- About dropdown – Consolidated Releases, Announcements, and Security Audit into a dropdown menu in the header navigation.
- Dark mode icons – Added CSS overrides for audience and learn-more icon classes.
- Doc page styling – New
doclayout wraps markdown content pages in styled cards with proper typography. - Proxy package link – Added
reso-web-api-proxyto the Pages workflow (was missing, causing a broken link).
Testing
- 928 tests across 7 packages, all passing
- All 3 compliance suites pass (PostgreSQL, MongoDB, SQLite)
v0.3 – 2026-04-03
The “Cache Me If You Can” Release
Third milestone release adding OAuth2 Client Credentials authentication, lazy lookup loading, IndexedDB metadata caching, a standalone CORS proxy package, and improved error handling across the application.
OAuth2 Client Credentials
- Token fetch via proxy – Client Credentials token requests route through
/api/proxyto avoid CORS restrictions. Supports body, header (Basic), and query credential transport modes. - Per-server token storage – Access tokens stored per server ID in Electron secure storage (encrypted) or browser sessionStorage. Tokens survive page reloads.
- Proxy availability detection – Client Credentials auth mode disabled in the connection modal when no proxy backend is detected.
- Token readiness gating – Data and metadata requests wait for token availability on Client Credentials servers, preventing 401 race conditions.
Performance
- Lazy lookup loading – Lookups fetched on demand per field or group instead of loading all enum values upfront. BasicSearch fetches only its 2-3 enum fields; AdvancedSearch fetches per expanded group.
- Batch lookup fetch –
LookupName in (...)queries withPrefer: odata.maxpagesizereplace individual per-field requests. Configurable viamaxPageSizeonLookupResolverConfig. - IndexedDB schema cache – Parsed CSDL schemas cached with gzip compression and 24-hour TTL. Eliminates repeated
$metadatafetches that trigger rate limiting. - IndexedDB lookup cache – Lookup values cached with 1-hour TTL per server per lookup name.
- Analytics-driven search fields – BasicSearch uses
summary-fields.json(RESO adoption data) to pick the most relevant fields for each resource. - Per-server metadata refresh – Stale-while-revalidate: only replaces cached metadata if the fresh fetch succeeds and parses.
Standalone CORS Proxy (New Package)
@reso-standards/web-api-proxy– Lightweight Express server with CORS proxy, health endpoint, static file serving, and SPA fallback.- Three deployment modes – Standalone CLI, Express middleware, or Docker container.
- Desktop client fallback – Server entry tries the reference server first, falls back to proxy-only mode when unavailable.
Error Handling
- Centralized
formatError– Maps HTTP status codes to human-readable titles and descriptions. Handles 429 rate limiting, 5xx server errors, auth failures, timeouts, and network errors. FriendlyErrorcomponent – Consistent error display across all pages with icon, quip, description, server response, request URL with copy button, and navigation buttons.- Request URL display – Shows the actual target URL (proxy unpacked) on error pages with copy-to-clipboard.
Search and UI
- Filters button – Direct access to Advanced Search from the search bar. Filter count badge using the OData expression parser.
- Apply Filters fix – Combined URL param update prevents the filter from being lost when closing the advanced search panel.
- OData filter editor – Inline toggle between basic search fields and raw OData filter input with copy button.
- Date picker – Native
<input type="date">for timestamp fields with dark mode support. - ModificationTimestamp – Always included in search fields and sort buttons when the server supports it.
- Error navigation – Sticky footer on Add/Edit forms with Previous/Next to walk through validation errors. Scrolls to first error on submit using MutationObserver for collapsed panels.
- Collection enum toggle buttons – Multi-value enum fields render as toggle buttons on Add/Edit forms, stored as arrays per OData standard.
- Media carousel – Loading indicator with 50ms delay for slow images. Broken image placeholder with landscape icon.
- Metadata timestamp – Shows when metadata was last fetched with per-server refresh button.
- Clear Metadata Cache – Connection manager button to clear all cached metadata.
- Resource titles – “Property Resource” instead of just “Property” on page headers.
Testing
- 928 unit/integration tests across 7 packages (up from 856 in v0.2)
- 72 web client tests (new) –
getLookupName,buildSearchFields,humanizeError,formatError,unpackRequestUrl, filter condition counting - 4 compliance suites pass on PostgreSQL
- GitHub issue created for E2E tests with Playwright (RESOStandards/reso-tools#75)
Desktop Client Downloads
Pre-built binaries are available on the GitHub Releases page. These binaries are unsigned and your operating system may warn you before running them.
- macOS (.dmg) – On macOS Sequoia and earlier, right-click the app and select Open, then click Open in the confirmation dialog. On macOS Tahoe (26+), unsigned apps are blocked by Gatekeeper. After mounting the DMG and copying the app to Applications, run:
xattr -cr "/Applications/RESO Desktop Client.app"to remove the quarantine attribute. - Windows (.exe) – Windows Defender SmartScreen may show “Windows protected your PC.” Click “More info” then “Run anyway.”
- Linux (.AppImage, .deb) – AppImage:
chmod +xthe file and run it. Deb: install withsudo dpkg -i.
The app checks for updates on launch and from the Help > Check for Updates menu. When a new version is available, you will be prompted to download it.
Other
odata-clientrenamed toreso-client(@reso-standards/reso-client)- DD documentation migrated to standalone repo (RESOStandards/reso-data-dictionary-documentation)
- Fixed DD docs 404s for lookup values with spaces in directory names
- Added Usage column to DD docs Used By tables on LookupName pages
v0.2 – 2026-03-10
RESO Desktop Client and Other Improvements
Second milestone release introducing a native desktop application, multi-server connectivity, a full-featured metadata explorer, and comprehensive compliance testing across all four RESO certification suites.
Desktop Client (New)
- Electron desktop application – Native app wrapping the reference server and UI for macOS, Windows, and Linux. Dual-process architecture: CJS main process for window management, ESM child process for the server (avoids Electron’s CJS/ESM interop issues).
- Native menus and navigation – Full menu bar (File, Edit, View, Navigate, Window, Help), keyboard shortcuts (Cmd/Ctrl+[/] for back/forward), three-finger swipe gestures, and trackpad scroll navigation.
- RESO-branded icons – Custom
.icns(macOS),.ico(Windows), and.png(Linux) app icons. - Automatic port discovery – Server starts on a random available port and communicates readiness via IPC.
- SQLite backend – Persistent storage in the user data directory, no external database required.
Server Switcher & External Server Support
- Multi-server connectivity – New server switcher component allows connecting to external OData servers alongside the built-in reference server. Supports Bearer token and Client Credentials OAuth2 authentication.
- Server connection modal – Add/edit/remove server connections with live connection testing. Stored in React context with localStorage persistence.
- Server-aware proxy – Backend proxy endpoint forwards OData requests to external servers, handling authentication and CORS transparently.
- Metadata adapter – Translates external server CSDL metadata into the internal format, enabling the full UI (search, detail, CRUD) against any OData-compliant server.
- Granular permissions – Server context tracks per-server capabilities (read, create, update, delete) and the UI adapts accordingly (hides Add/Edit/Delete for read-only servers).
UI Enhancements
- Landing page – New home page with server status, resource overview cards, and quick navigation.
- Metadata Explorer – Browse entity types, fields, navigation properties, and enumerations from the server’s
$metadata. Searchable and filterable. - Mobile responsive layout – Collapsible sidebar, responsive grid layouts, and touch-friendly controls.
- Advanced search overlay – Full-screen search panel with field-type-aware inputs, date pickers, and lookup dropdowns.
- Human-friendly lookups – Unified lookup resolver translates enum values to display names throughout the UI.
- Loading spinner – Consistent loading state across all pages.
- Password field masking – Sensitive fields (tokens, passwords) masked by default with toggle to reveal.
- Dynamic key discovery – Detail/edit pages work with any key field, not just hardcoded
ListingKey. - Generic media display – Media carousel works with any resource that has navigation to Media, not just Property.
- Organizations page – Browse the RESO member directory with multi-select filters (type, location, status), sortable columns, sticky table header, zebra-striped rows, and expandable detail rows with endorsements, address, certification link, and embedded OpenStreetMap.
- Friendly error pages – Reusable
FriendlyErrorcomponent and redesigned 404 page with real-estate quips, styled server message blocks, navigation buttons, and Contact Support link. Applied consistently across all page-level error states. - Sidebar navigation polish – Added Organizations link, replaced chevron arrows with semantic icons (search for Resources, code brackets for Metadata, gear for Admin, chart for Data Generator), renamed “Metadata Explorer” to “Metadata”, consistent spacing via
space-y-4. - Metadata filter labels – Renamed “Properties” filter to “Fields” to avoid ambiguity with the Property resource in real estate contexts.
Data Dictionary Documentation Site
- Sticky column headers – FIELD/DEFINITION/TYPE/USAGE headers stick below the sort controls as you scroll through fields, in both grouped and flat views.
- Group tree improvements – Sidebar group tree syncs with content: hides when flat-sorting, reappears when Show Groups is toggled. Groups with subgroups show expand/collapse chevrons. Clicking a group scrolls to it with correct offset accounting for sticky headers.
- Parent-first group ordering – Parent group fields now render before subgroups (e.g., Structure fields appear before Structure > Performance > GreenMarketing).
- Cross-reference links on lookup values – The References field on lookup value pages now links to property type cross-reference pages.
- Terms and Definitions link – Every resource, field, and lookup page links to the version-specific terms glossary.
- Mobile layout overhaul – Sticky resource header pins breadcrumb, title, definition and sort controls below the nav bar. Sort pills replaced with a compact dropdown + direction toggle. Sticky column headers in both grouped and ungrouped modes. StandardName truncation with ellipsis. Definition callout collapses to 2 lines with overflow-aware “… more” toggle.
- Dark/light mode toggle – Moved out of the hamburger menu and placed in the header bar, always accessible.
- 404 page – Real estate dad jokes with a “tell me another one” button.
- Copy buttons on detail pages – Clipboard icons on Standard Name and Display Name (fields) and Lookup Name, Standard Value, and Legacy OData Value (lookups).
- Show Groups toggle – Now a proper toggle: click to activate groups, click again to deactivate and revert to flat Name sort.
Monorepo Restructuring
- Flat package layout – Extracted
reso-web-client/andreso-desktop-client/to top-level packages (previously nested underreso-reference-server/ui/andreso-reference-server/desktop/). - Updated READMEs – All package READMEs reviewed and updated for the new layout: fixed stale paths, LICENSE links, package references, and CLI examples.
- Desktop client documentation – Added prerequisites, local dev quick-start, architecture overview, packaging notes, and connection storage details.
OData Client Improvements
- HTTP keep-alive and gzip compression – Connection pooling and response compression for faster metadata and data fetching.
- CSDL parser enhancements – Extended parser handles navigation property bindings, enum type members, and complex type definitions from external servers.
- Lookup resolver library – New
@reso-standards/reso-clientexport for resolving human-friendly lookup values from CSDL metadata.
Compliance Testing
All four RESO certification suites pass against the reference server with PostgreSQL backend and string enumerations:
| Suite | Result |
|---|---|
| Data Dictionary 2.0 | 1034 passed, 0 failed (10,390 Property records) |
| Web API Core 2.0 | 42 passed, 0 failed |
| Add/Edit (RCP-010) | All passed |
| EntityEvent (RCP-027) | All passed, 1000 events validated |
- Docker compliance infrastructure – Added data seeding to DD and Web API Core compliance entrypoints. Fixed Dockerfile runtime stage to include server config files required by
import.meta.dirnameresolution. - Fixed OData
$skiphandling –$topis now correctly treated as a per-page limit per the OData spec, independent of$skip. Previously,$top=5&$skip=5returned empty results because the handler calculatedremaining = 5-5 = 0.
CI/CD
- Compliance workflow on PR – GitHub Actions runs all four compliance suites (DD 2.0, Web API Core, Add/Edit, EntityEvent) against PostgreSQL, MongoDB, and SQLite on every pull request. Seeds 1K Property records per backend.
- GitHub Pages on merge – Pages workflow triggers on merge to
main(was temporarily set tov0.2during development). DD documentation auto-generated from CSV data.
Housekeeping
- Root CLAUDE.md – Added project-wide coding standards (functional/declarative, no classes, no
any, arrow functions, immutability). Removed redundant package-level CLAUDE.md files. - LICENSE – Renamed from
License.txt; updated all package README references. - README badges – Tests (856 passed), coverage (97%), RESO compliance (4/4 suites).
- Doc/ cleanup – Removed stale project summary files.
Security
- v0.2 security audit – 11 new findings across desktop client, server switcher, proxy, and UI. 3 High, 5 Medium, 3 Low severity.
- 5 prior findings confirmed fixed – Token expiry, timing-safe auth, LIKE escaping, key interpolation, and security headers (matching closed GitHub issues).
Bug Fixes
- Fix detail page summary fields – Summary section now displays correctly with proper field resolution.
- Fix expansion card layout – Related record cards handle non-entity-set expansions with key field fallback.
- Fix search lookups – Lookup fields in search filters resolve to correct display values.
- Fix MongoDB Add/Edit compliance – CRUD operations work correctly with MongoDB backend.
- Remove
ListPrice >= ListPriceLowcross-field rule – The comparison prevented setting a low list price. Both fields are now validated independently as > 0, which is sufficient. A proper RESO Validation Expressions engine will replace hardcoded rules in a future release.
| SHA | Description |
|---|---|
| 0f4715b | Add HTTP keep-alive and gzip compression to OData client |
| fa06efa | Add server switcher, external OData server support, and MongoDB Add/Edit compliance |
| 3039ada | Add generic media display, navigation guard, and resource-aware data generation |
| de0c1e7 | Add granular server permissions, static DD field groups, and external server UI improvements |
| d90f81f | Add unified lookup resolver, loading spinner, and permission-aware nav |
| a2de8e2 | Add Metadata Explorer, dynamic key discovery, and human-friendly lookups |
| 65cfa3c | Fix detail page summary fields, expansion card layout, and search lookups |
| 982d98d | Add key field fallback for non-entity-set expansion cards |
| a9e8620 | Fix security findings: token expiry, timing-safe auth, LIKE escaping, key interpolation, security headers |
| fb4a383 | Update SECURITY_AUDIT.md: mark 5 findings as fixed |
| fbd0b3c | Add landing page, mobile responsive layout, and password field masking |
| d2b5bca | Improve UI: advanced search overlay, landing page fixes, error messages, and dev proxy |
| 9344ebd | Add Electron desktop client with native menus, navigation, and app icons |
v0.1 – 2026-03-06
UI Polish, Developer Experience, and Compliance Fixes
First milestone release focusing on UI improvements, developer workflow enhancements, GitHub Pages documentation, and compliance test fixes.
UI Improvements
- Pin action buttons to bottom of Add/Edit pages (#20) – Submit buttons stay visible via
sticky bottom-0as users scroll through long forms. Pinned header with back navigation and page title at the top. - Fix detail page layout – Restored side-by-side summary + media carousel using flexbox (
lg:flex-row). Field groups render in two columns instead of one. - Zebra striping on Related Records – Alternating row backgrounds in expanded navigation property panels for improved readability.
Developer Experience
- Docker hot-reload dev mode – New
docker-compose.dev.ymloverlay runs the Vite dev server inside Docker with source volume mounts. Edits toui/src/are reflected instantly without container rebuilds. - Smart Vite proxy routing – Rewrote
vite.config.tsto mirror nginx routing logic: bare resource paths serve the SPA on browser navigation but proxy to the API forfetch()requests (Accept: application/json). Fixes the JSON-on-refresh bug in dev mode.
Documentation & Infrastructure
- RESO-branded GitHub Pages site – Jekyll site in
.github/pages/with custom layout matching certification.reso.org design (navy header, card-based content, green/blue badges). GitHub Actions workflow auto-syncs package READMEs into the site on every push to main.
Compliance Fixes
- Fix DD 2.0 schema validation errors – Navigation properties (
HistoryTransactional,SocialMedia) were leaking as empty arrays in MongoDB Property responses. ExcludedisExpansionfields from collection field coercion. - Fix SQLite readonly database – Created
/datadirectory with correct ownership in Dockerfile so the non-rootresouser can write to mounted volumes. - Fix Add/Edit compliance entrypoint – Added
add-editsubcommand after certification package consolidation. - All three backends (PostgreSQL, MongoDB, SQLite) now pass DD 2.0 compliance with 0 schema validation errors.
| SHA | Description |
|---|---|
| 38dfa81 | Pin action buttons to bottom of Add/Edit pages (#20) |
| 295fcd0 | Fix detail page layout: side-by-side summary + media, two-column field groups |
| 970359c | Add zebra striping to Related Records expanded panels |
| 9c31c5b | Add Docker hot-reload dev mode and fix Vite SPA proxy routing |
| 408d3d6 | Add RESO-branded GitHub Pages site with auto-synced package docs |
| 1904b71 | Fix DD 2.0 schema validation errors and SQLite readonly issue |
v0.0.28 – 2026-03-06
EntityEvent Compliance Testing Tool (#44)
Native TypeScript compliance testing tool for the RESO EntityEvent Resource (RCP-027). Validates that servers correctly implement change tracking via monotonically increasing sequence numbers.
Two testing modes:
- Observe mode – read-only, for third-party servers. Polls for new events with
configurable timeout (
--poll-timeout). Tests 8 scenarios + incremental sync via polling. - Full mode – write access, for reference server or servers with a writable canary resource. Creates/updates/deletes records and verifies corresponding EntityEvent entries. Tests all 11 scenarios.
Scenarios (11 total):
- Both modes:
metadata-valid,read-only-enforced,event-structure,sequence-monotonic,query-filter,query-orderby-top-skip,query-count,incremental-sync - Full mode only:
create-triggers-event,update-triggers-event,delete-triggers-event
Data validation:
- Batch-fetches records referenced by EntityEvent entries using OData
inoperator (configurable batch size, max 1000 events) - Validates each record’s fields against
$metadatausing a new lightweight Edm type checker - Tracks 404s (deleted records), validates
ResourceRecordUrlwhen present --strictflag: unknown fields are failures (default: warnings)
Lightweight Edm type checker (edm-validator.ts):
- Shared infrastructure in
test-runner/– reusable by future compliance tools - Validates:
Edm.String,Boolean,Int16/32/64,Decimal/Double/Single,DateTimeOffset,Date,TimeOfDay,Guid,Binary,Duration,Collection(X) - Non-Edm types (enum types) return
skipstatus – full enum validation deferred to DD compliance tool (#42)
Compliance report generation:
- Per-scenario details with mode classification (
both/full), pass/fail status, failures - Data validation summary: total events, unique resources, sequence range, not-found keys
- Human-readable remarks with mode, event count, resources, and failure summary
CLI:
- Restructured from flat command to subcommands:
reso-cert add-edit,reso-cert entity-event reso-cert entity-eventoptions:--url,--mode,--writable-resource,--payloads-dir,--max-events,--batch-size,--poll-interval,--poll-timeout,--strict,--mock,--compliance-report,--spec-version, plus auth options
Mock server:
- Express-based mock OData server for test isolation (same pattern as Add/Edit tool)
- In-memory EntityEvent store with auto-incrementing sequence
- Canary resource CRUD that triggers EntityEvent writes
- Simple OData
$filterparser (eq,gt,inoperators,andcombinator)
New files (10):
src/entity-event/types.ts– config, mode, scenario names, record shape, report typessrc/entity-event/test-runner.ts– scenario runner for both modessrc/entity-event/data-validator.ts– batch record fetching and Edm validationsrc/entity-event/compliance-report.ts– report generationsrc/entity-event/index.ts– public exportssrc/entity-event/mock/handlers.ts– mock server request handlerssrc/entity-event/mock/server.ts– mock server setup/teardownsrc/test-runner/edm-validator.ts– shared Edm type checkertests/edm-validator.test.ts– 30 tests for Edm validatortests/entity-event-runner.test.ts– 6 integration tests with mock servertests/entity-event-compliance-report.test.ts– 6 report generation tests
Tests:
- 42 new tests (30 edm-validator + 6 runner + 6 compliance-report)
- 102 total certification tests (up from 60)
- 777 total across all packages
v0.0.27 – 2026-03-06
EntityEvent Resource – RCP-27 (#43)
Adds the RESO EntityEvent Resource for change tracking via monotonically increasing sequence numbers. Every Create, Update, or Delete operation on the server automatically writes an EntityEvent record with the affected ResourceName, ResourceRecordKey, and a database-managed auto-increment EntityEventSequence.
Core implementation:
- DAL decorator pattern – wraps any
DataAccessLayerto intercept all writes (including seeding and admin data generation) without modifying handler code - Database-native auto-increment sequences: PostgreSQL
BIGSERIAL, SQLiteAUTOINCREMENT, MongoDB atomic counter collection EntityEventWriterinterface – backend-agnostic abstraction for event persistence- EntityEvent exposed as read-only OData resource (GET collection + GET by key,
$filter,$orderby,$top,$skip,$count– no POST/PATCH/DELETE) - Opt-in via
ENTITY_EVENT=trueenvironment variable (disabled by default)
Compaction:
CompactionRunnerremoves duplicate (ResourceName, ResourceRecordKey) entries, keeping only the highest EntityEventSequence per pair- Scheduled via
setIntervalwith configurable interval (COMPACTION_INTERVAL_MS, default 1 hour, 0 = disabled) - PostgreSQL/SQLite: single
DELETE ... NOT IN (SELECT MAX(...) GROUP BY ...) - MongoDB:
$groupaggregation +deleteMany
Configuration:
ENTITY_EVENT=true– enable EntityEvent trackingENTITY_EVENT_RESOURCE_RECORD_URL=true– include optional ResourceRecordUrl fieldCOMPACTION_INTERVAL_MS=3600000– compaction interval (default 1 hour)
Docker Compose:
- All three server services (PostgreSQL, MongoDB, SQLite) pass through
ENTITY_EVENTvia${ENTITY_EVENT:-false}– enable from the shell:ENTITY_EVENT=true docker compose --profile mongodb up -d mongodb server-mongo ui-mongo
Schema:
- PostgreSQL:
BIGSERIAL NOT NULL PRIMARY KEY+idx_EntityEvent_resourceindex - SQLite:
INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT+ existing FK index pattern - MongoDB:
counterscollection with atomic sequence + unique index on EntityEventSequence
Tests:
- 17 new tests covering DAL decorator (insert/update/delete event capture, recursion guard, passthrough reads, URL encoding), compaction (timer, stop, error handling), and schema generation (BIGSERIAL, AUTOINCREMENT, index)
- 215 total reference server tests (198 existing + 17 new)
- 735 total across all packages
Security audit: No issues found. All SQL uses parameterized queries, MongoDB uses structured document operations, auth inherited from existing middleware, no new attack surface. npm audit: 5 moderate (dev-only: vitest/vite/esbuild).
v0.0.26 – 2026-03-06
Add/Edit Compliance Report + PATCH Validation Fix (#41)
Structured compliance report generation for the Add/Edit (RCP-010) endorsement, and a fix for PATCH (partial update) validation that was incorrectly enforcing required fields.
Add/Edit compliance report (#41):
- New
compliance-report.tsmodule generates a structured JSON report from test results for API submission - Report includes
description,version,generatedOn,outcome,remarks, and per-scenarioscenariosarray outcome:passedif all 8 scenarios pass,failedif any failremarks: human-friendly summary of operations tested (Create/Update/Delete), response modes (representation/minimal), resource name, and field counts- Per-scenario details: field names tested (excluding keys, no values for privacy), enumeration values (DD standard values, not sensitive), expansion names, and failure details (assertion, expected, actual)
- CLI flags:
--compliance-report <path>and--spec-version <version> - 11 new tests covering all report facets (60 total in add-edit package)
PATCH validation fix:
validateBusinessRulesnow acceptsskipRequiredparameter (defaultfalse)- Server update handler passes
skipRequired: truefor PATCH requests - Fixes 400 errors on valid partial updates that omitted required fields (City, StateOrProvince, PostalCode, Country)
Docker Add/Edit compliance:
- New
compliance-addeditDocker Compose service with entrypoint script - Fixed URL routing: server OData routes are at root (
/$metadata,/Property), not under/odata/prefix - Dockerfile for add-edit package added to support Docker builds
- All 8 Add/Edit scenarios pass with compliance report generated
All three compliance suites verified:
- Web API Core 2.0.0: 42 passed, 3 skipped, 0 failed
- Data Dictionary 2.0: 1,034 passed, 570 skipped, 0 failed, 0 variations
- Add/Edit (RCP-010): 8 passed, 0 failed
v0.0.25 – 2026-03-05
UI: Human-Friendly Field Names, Detail Page Improvements (#40)
Display human-friendly field names throughout the UI using
RESO.OData.Metadata.StandardName annotations, with fallback to raw field names.
Human-friendly display names (#40):
- Added
getDisplayName(field)andgetDisplayNameFromMap(fieldName, fieldMap)utilities to extractStandardNameannotations – O(1) viaMap.get()lookup - Applied across all UI surfaces: detail pages, search results cards, advanced search form, add/edit form labels, sort-by buttons
- Labels show display name with tooltip; raw
fieldNameno longer exposed in UI
Detail page layout improvements:
- Enum array values render as bordered pill chips instead of truncated comma-separated strings
- Zebra-striped rows on field lists (light/dark mode compatible)
- Media preview floated right with fields flowing around and below it, replacing the rigid 50/50 split – gives fields full width and eliminates value truncation
- Summary header (key, timestamp, address) separated into its own card
- Tooltips on all truncated labels and values site-wide (detail, results cards, advanced search, form inputs)
Data generator fix:
- Number-of-records input now allows clearing and retyping (was stuck at 1)
- Added inline validation with error messages for non-numeric, fractional, or out-of-range values
v0.0.24 – 2026-03-05
DD 2.0 Compliance: Collection Nulls + Lookup/Data Generator Sync (#32)
Fixed all 2,012 DD 2.0 schema validation errors discovered during compliance testing with human-friendly string enumerations and the Lookup Resource.
Collection field null → empty array (1,869 errors):
- PostgreSQL/SQLite (
queries.ts):deserializeValuenow returns[]for null collection fields instead ofnull - MongoDB (
mongo-dal.ts): AddedcoerceCollectionspost-processing to replace null/missing collection fields with[]in bothqueryCollectionandreadByKey - Affects 93 collection-valued fields (Appliances, Heating, Cooling, etc.)
Data generator / Lookup Resource value sync (143 errors):
PropertyType,PropertySubType: Generator now draws from DD lookup map values instead of hardcoded arrays, with hardcoded fallback when no lookups existMemberDesignation: Same pattern – prefers DD lookup values over hardcoded listCity: Added 15 real city names toserver-metadata.jsonlookup entries (was a single placeholder)StreetSuffix: Added 10 real suffixes toserver-metadata.jsonlookup entries (was a single placeholder)- Total lookups: 3,634 (up from 3,611)
DD 2.0 compliance result: 1,034 passed, 0 failed, 0 schema validation errors, 0 variations. Full compliance achieved on PostgreSQL with string enumerations.
Test counts: 198 server tests, 104 data generator tests – all passing.
v0.0.23 – 2026-03-05
EnumType Mode – OData Edm.EnumType Support (#30, #27)
Added ENUM_MODE=enum-type as an alternative to string enumerations. When
enabled, the server uses OData Edm.EnumType definitions in EDMX metadata
instead of Edm.String with LookupName annotations.
EDMX metadata ($metadata):
- Second Schema block (
org.reso.metadata.enums) with<EnumType>definitions - Enum fields reference fully-qualified type names (e.g.,
org.reso.metadata.enums.StandardStatus) instead ofEdm.String - Collection enum fields use
Collection(org.reso.metadata.enums.X)syntax LookupNameannotations omitted in enum-type mode- No Lookup Resource exposed (only needed for string mode)
- EnumType members use PascalCase
lookupValueidentifiers with sequential integer values (0, 1, 2, …) - Only enum types referenced by active resources are included
- Members validated against OData SimpleIdentifier rules
Compliance testing:
- RESOScript generator (
generate-resoscripts.sh) updated for dual-mode field extraction – matchesType="org.reso.metadata.enums.*"in enum-type mode vsEdm.StringwithLookupNamein string mode - Namespace parameters (
SingleValueLookupNamespace,MultipleValueLookupNamespace) populated from EDMX type attributes - Commander flag
-DuseStringEnums=trueconditionally omitted in enum-type mode - Docker Compose compliance services accept
ENUM_MODEenv var override
Configuration:
ENUM_MODE=enum-typeselects EnumType mode (default remainsstring)- Run compliance with enum-type:
ENUM_MODE=enum-type docker compose --profile compliance-core up
Tests: 9 new enum-type EDMX tests (198 total).
v0.0.22 – 2026-03-05
SQLite Data Access Layer Backend (#3)
Added SQLite as a third database backend alongside PostgreSQL and MongoDB. Lightweight option requiring no external database – ideal for local development and testing.
New files:
filter-to-sqlite.ts– OData $filter to SQLite SQL translator (32 tests)sqlite-schema-generator.ts– Edm type to SQLite type mapping + DDL (14 tests)sqlite-dal.ts– FullDataAccessLayerimplementation with CTE + LEFT JOIN for$expand,json_each()for collection lambda expressions,strftime()for date functions, andREGEXPformatchesPattern()sqlite-pool.ts– Database handle factory with WAL journal mode
Configuration:
DB_BACKEND=sqliteselects the SQLite backendSQLITE_DB_PATHsets the database file path (default:./reso_reference.db)
Docker Compose:
docker compose --profile sqlite up -d– starts server + UI with SQLitedocker compose --profile sqlite --profile seed-sqlite up– seeds test data- Compliance profiles:
compliance-dd-sqlite,compliance-core-sqlite
Compliance: Web API Core 2.0.0 – 42/42 passed, 3 skipped (identical to PostgreSQL and MongoDB).
v0.0.21 – 2026-03-04
Lookup Resource + Human-Friendly String Enumerations (#29)
The server now exposes a Lookup Resource per DD 2.0 Section 2.2 and uses
human-friendly StandardName display values for string enumerations. This is
required for DD 2.0 compliance when using Edm.String enumeration mode.
Lookup Resource:
- New
Lookupentity set with 6 fields:LookupKey,LookupName,LookupValue,StandardLookupValue,LegacyODataValue,ModificationTimestamp LookupKeyis a deterministic SHA-3 256 hash of{LookupName}:{LookupValue}- Auto-seeded from
server-metadata.jsonat startup (3,611 records) - Read-only: GET collection + GET by key with full OData query support
(
$filter,$top,$skip,$count,$select,$orderby) - POST/PATCH/DELETE routes not registered (404)
Enumeration mode (ENUM_MODE):
-
New ENUM_MODEenvironment variable (stringenum-type, defaultstring) - String mode (default):
Edm.Stringtypes withLookupNameannotations, Lookup Resource exposed, human-friendly values in data (e.g.,"Active Under Contract"instead of"ActiveUnderContract") - EnumType mode: future ticket (#30)
Data generator:
- New
transformLookupsForHumanFriendly()transforms lookup maps so all downstream generators produce human-friendly values without code changes getLookupDisplayValue()resolvesStandardNameannotation when available, falls back tolookupValuefor the 23 lookups without annotations
MongoDB filter fixes:
$neoperator now uses$nin: [value, null]to exclude null/missing documents, matching SQL three-valued NULL logicall()lambda now uses$not: { $elemMatch: { $ne: value } }for correct “every element matches” semantics (was using$allwhich is set containment)
RESOScript generator improvements:
- Decimal and date fields use median of distinct values (prevents
gt/lttests from failing when the first value is an extreme) - Multi-value lookup
MultipleLookupValue1prefers single-element collection records soall()tests find matching data
UI:
- Lookup added to resource list for browsing/searching
- Add/Edit/Delete buttons hidden for read-only resources (Lookup)
- Nginx proxy allowlist updated
Compliance results (both PostgreSQL and MongoDB):
- Web API Core 2.0.0: 42 passed, 0 failed, 3 skipped
v0.0.20 – 2026-03-04
Web API Core 2.0.0 Compliance – All Tests Passing (#19)
The reference server now passes all applicable Web API Core 2.0.0 compliance
tests: 42 passed, 0 failed, 3 skipped (the 3 skipped are has operator
tests, not applicable when using string enumerations).
Server fixes:
- PostgreSQL numeric coercion –
pgdriver returns BIGINT/NUMERIC as strings;deserializeValue()now coerces Edm.Int* to JavaScript integers and Edm.Decimal/Double/Single to JavaScript decimals - Edm.Date truncation – Edm.Date fields now return ISO 8601 date-only
(
YYYY-MM-DD), not full timestamps, in API responses - Lambda
any()/all()support – OData collection lambda expressions translated to PostgreSQL JSONB containment (@>,<@) and MongoDB native array operators ($in,$all), with 6 new tests - Service document endpoint –
GET /returns the OData service document
RESOScript generator improvements:
- Date values truncated to ISO 8601 date-only format
- Timestamp field selection prefers fully-populated DateTimeOffset fields with fallback to ModificationTimestamp (always populated per RESO rules)
- Integer field selection uses median of distinct values (ensures
lt/gttests find matching records) - Multi-value lookup fields correctly populated for
any()/all()tests
Filter parser:
- Rebuilt
dist/with fractional seconds support in DateTimeOffset lexer
v0.0.19 – 2026-03-04
UI: Expansion Cards for Navigation Properties (#25)
The detail page now displays expanded navigation property data in a collapsible “Related Records” section. Each expansion renders as an inset card with a two-column, vertically scrollable field list (max 4 visible rows).
- To-one expansions (ListAgent, ListOffice, etc.): single card with alphabetized fields and “View” link to navigate to the related entity
- To-many expansions (Media, OpenHouse, Showing, etc.): card with back/next pagination to browse individual records
- Expansion fields are filtered from regular field lists (no more “[object Object]”)
- Existing
MediaCarousel(photo/video display) unchanged
Bug Fix: nginx proxy missing Teams, TeamMembers, OUID
The nginx reverse proxy allowlist in nginx.conf.template was missing three
resources added in v0.0.18 (Teams, TeamMembers, OUID). API requests for
these resources were served the SPA HTML instead of being proxied to the backend,
causing “not valid JSON” errors.
v0.0.18 – 2026-03-04
Data Generator: Referentially Correct Multi-Resource Seed Data (#24)
The data generator now produces records with valid cross-resource FK linkages so
$expand works for all to-one navigation properties (e.g., Property→ListAgent,
Member→Office, Office→OfficeBroker).
FK resolver (fk-resolver.ts):
- Discovers to-one FK relationships from metadata navigation bindings
- Builds a dependency graph and topologically sorts resources
- Detects and breaks the Office ↔ Member circular dependency via deferred back-fill
- 28 tests covering FK discovery, dependency graph, topo sort, and plan building
Multi-resource orchestrator (index.ts):
- New
generateWithDependencies()creates resources in dependency order with FK injection - Maintains a key pool per resource; injects valid FK values from previously created records
- Three output modes: HTTP POST (with server back-fill via
dal.update()), JSON files, curl scripts - Supports
resolveDependenciesflag inSeedOptions
PATCH support (client.ts):
patchRecordsViaHttp()for HTTP back-fill of deferred FKsupdateJsonRecords()for read-modify-write of JSON output files- Curl script generation extended with PATCH commands
CLI improvements:
--depsflag (default true) enables dependency resolution- Interactive mode shows plan summary: “Property (10) requires: Office (4), Member (10), …”
Server endpoint:
POST /admin/data-generatoracceptsresolveDependenciesin request body- When true, builds a multi-resource plan and executes phases with
dal.update()back-fill
Docker seed:
- Single seed call with
resolveDependencies: truereplaces separate per-resource calls - Verified: PostgreSQL (892 records, 0 errors) and MongoDB (892 records, 0 errors)
Bug fixes during testing:
- TaxAssessedValue: Edm.Int64 but generated as decimal – fixed with
Math.round() - PostgreSQL CTE $expand JOIN: parent-fk strategy used raw column name instead of CTE alias
- PostgreSQL CTE $expand: FK columns now included when
$selectis used with$expand - PostgreSQL $count with $top=0: window function returns nothing – added fallback
SELECT COUNT(*) - Removed hardcoded
OfficeBrokerKey = 'BRK0001'from office generator - UI data generator: added
resolveDependencies: trueto API call - Added Teams, TeamMembers, OUID to TARGET_RESOURCES in server and UI
Files changed: 20 files, ~760 insertions, ~130 deletions
v0.0.17 – 2026-03-04
UI: Pinned Search Toolbar and Detail Header (#23)
On search pages, the resource title, action buttons, search bar, advanced search panel, sort buttons, and result count now stay pinned at the top – only the result cards scroll. On detail pages, the back link, title, and Edit/Delete buttons stay pinned while summary, media, and field groups scroll beneath.
Layout change: <main> is now overflow-hidden and each page manages its own
scroll container. This enables per-page pinning without position: sticky hacks.
Affected files:
ui/src/components/layout.tsx– removed padding and scroll from<main>ui/src/pages/search-page.tsx– flex layout: pinned toolbar + scrollable resultsui/src/components/results-list.tsx– count display moved to search page pinned areaui/src/pages/detail-page.tsx– flex layout: pinned header + scrollable contentui/src/pages/add-page.tsx,edit-page.tsx,delete-page.tsx,not-found-page.tsx,admin/admin-layout.tsx– scroll wrappers with padding
v0.0.16 – 2026-03-04
Validation Improvements, Tax/Expense Data, and UI Fixes
Validation: Pattern-based field rules and negative coordinate fix (#22):
- Added
fieldPatterntoFieldRulefor regex-based field matching – expense, fee, and amount fields (matching/(?:Expense|Amount|Fee\d?)$/) now validated against a $0–$10,000 range without enumerating all 25+ field names - Price fields (ListPrice, OriginalListPrice, etc.) now require values > 0 (previously accepted 0)
- Latitude and Longitude exempt from the “must be >= 0” rule – negative coordinates are valid for Western/Southern hemispheres
- 11 new validation tests (expense patterns, negative coordinates)
Data generator: Realistic tax and expense fields:
- Added US state effective property tax rates (2024 ACS data, all 50 states)
TaxAnnualAmountcalculated from ListPrice × state rate with ±10% randomizationTaxAssessedValuegenerated at 70–95% of ListPriceTaxYearset to current or previous year- 10 expense fields generated with realistic ranges: AssociationFee, InsuranceExpense, ElectricExpense, WaterSewerExpense, TrashExpense, CableTvExpense, MaintenanceExpense, OperatingExpense, OtherExpense, AssociationFee2
- 5 new data generator tests
UI: Auto-expand field groups with validation errors (#22):
FieldGroupSectionnow accepts anerrorCountprop – groups auto-expand when errors are present, with a red error count badge on the section header and a red border highlightRecordFormcomputes per-group error counts viauseMemoand passes them to eachFieldGroupSection- Only affects resources with field groups (e.g., Property)
UI: Dark mode persistence fix:
- Dark mode preference now persists via
localStorageinstead of URL query params, which were lost on React Router navigation - URL
?theme=dark|lightstill works as a one-time override
Affected files:
validation/src/business-rules/types.ts–fieldPatternonFieldRulevalidation/src/business-rules/property-rules.ts– expense pattern rule, price min > 0validation/src/business-rules/index.ts– pattern rule matching logicvalidation/src/metadata/validate.ts–ALLOW_NEGATIVE_FIELDSsetvalidation/tests/business-rules.test.ts– 9 new expense testsvalidation/tests/validate.test.ts– 2 new coordinate testsdata-generator/src/generators/property.ts– state tax rates, expense generationdata-generator/tests/generators.test.ts– 5 new testsui/src/components/field-group-section.tsx– errorCount prop, auto-expandui/src/components/record-form.tsx– per-group error countsui/src/hooks/use-dark-mode.ts– localStorage persistence
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
103 |
@reso-standards/odata-expression-parser |
152 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
76 |
@reso-standards/reference-server |
137 |
@reso-standards/certification-add-edit |
49 |
| Total | 618 |
v0.0.15 – 2026-03-05
DD 2.0 Compliance and Expansion Field Fixes
Achieved full RESO Data Dictionary 2.0 compliance (928 passed, 676 skipped, 0 variations, 0 schema validation errors) and fixed several OData spec issues.
Metadata source:
- Replaced
server-metadata.jsonwith thereso-certification-etlDD 2.0 reference (metadata-report.jsondated 2024-10-15), fixing 6 field name casing issues (ID→Id) that caused compliance variations - Same 1,727 fields; 3,611 lookups (355 more than previous source)
- Users can still pass their own metadata via the
METADATA_PATHenv var
Expansion field lazy loading:
- Server no longer returns expansion fields (e.g., HistoryTransactional,
SocialMedia) in responses unless explicitly requested via
$expand - Fixed in both PostgreSQL and MongoDB DALs at all levels: parent queries, readByKey, and navigation property sub-queries
- 7 new tests verifying expansion field filtering behavior
Data generator DD 2.0 compliance:
- Removed
MimeTypefrom Media generator (not a DD 2.0 field) - Removed
ResourceName/ResourceRecordKeyfrom OpenHouse and Showing generators (those resources useListingKeyfor parent FK) - Fixed Showing field names:
ShowingStartTime→ShowingStartTimestamp,ShowingEndTime→ShowingEndTimestamp, removedShowingDateandShowingInstructions(not in DD 2.0) - Added
isExpansionflag toResoFieldtype; field generator skips expansion fields
OData filter parser:
- Fixed DateTimeOffset lexer regex to handle fractional seconds
(e.g.,
2026-03-04T13:02:21.582Z) – previously only matched whole seconds
Other fixes:
- EDMX generator filters out expansion fields from NavigationProperty output
- Mock OAuth endpoint now parses
application/x-www-form-urlencodedbodies - OData-Version response header added to all responses
- Docker Compose: added 4 compliance services (DD + Core × Postgres + MongoDB) with profiles and result volumes
Compliance infrastructure (new compliance/ directory):
Dockerfile.dd– reso-certification-utils v3.0.0 (multi-stage build)Dockerfile.core– web-api-commander (Gradle build)dd-config.json– DD 2.0 config with bearer token and client credentialsentrypoint-dd.sh– wait for server, run DD tests (supportsRECORD_LIMIT)entrypoint-core.sh– wait for server, generate RESOScripts, run Core tests
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
91 |
@reso-standards/odata-expression-parser |
152 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
73 |
@reso-standards/reference-server |
137 |
@reso-standards/certification-add-edit |
49 |
| Total | 603 |
v0.0.14 – 2026-03-04
EDMX Metadata: EntityContainer and Nullable Fixes
Fixed two OData spec compliance issues in the generated EDMX XML metadata
(/$metadata endpoint).
EntityContainer with EntitySets:
- Added
<EntityContainer Name="Default">with an<EntitySet>element for each target resource, as required by the OData CSDL specification - EntitySets include
<NavigationPropertyBinding>entries linking navigation properties to their target EntitySets (only for targets included in the resource list)
Nullable attribute corrections:
- Removed
Nullable="true"from all properties – this is the OData default for non-collection properties and should not be emitted - Collection properties now always emit
Nullable="false"since they return the empty list[]instead ofnull
Modified files:
edmx-generator.ts– EntityContainer generation, Nullable logicedmx-generator.test.ts– 4 new tests (EntityContainer, EntitySet, NavigationPropertyBinding, Nullable)
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
91 |
@reso-standards/odata-expression-parser |
151 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
130 |
@reso-standards/certification-add-edit |
49 |
| Total | 593 |
v0.0.13 – 2026-03-04
OData $filter Two-Way Sync and Validation
Added two-way synchronization between the search bar and advanced search form, client and server-side filter validation, and an AST serializer for the filter parser.
AST serializer (astToFilterString):
- New
serializer.tsin@reso-standards/odata-expression-parser– walks the AST and produces a canonical OData$filterstring - Handles all node types: comparison, logical, not, arithmetic, function, lambda, literal, property, collection
- 54 round-trip tests (parse → serialize → compare)
Filter sync utility (filter-sync.ts):
parseFilterToEntries()– parses an OData filter string into form-compatible entries using the AST, detecting simple comparisons,contains(), lambdaany/allpatterns, and flagging unrepresentable expressionsbuildFilterString()– moved from advanced-search, builds OData filter from form entries
Search page state management:
draftFilterstate lifted to SearchPage as single source of truth- URL
$filterparam synced on browser back/forward - Client-side validation via
parseFilter()blocks invalid filters with error display before API calls - SearchBar is now a fully controlled component with validation error display and copy-to-clipboard for long filters
Advanced search two-way sync:
- Form state derived from incoming filter string via
parseFilterToEntries() lastEmittedRefprevents infinite update loops in bidirectional sync- Live sync: field changes rebuild filter string and update search bar
- Amber info bar when filter contains expressions the form cannot represent
- Expansion fields disabled with “filtering not yet supported” message
Server-side validation:
collectionHandlerreturns 400 (not 500) forParseError,LexerError, and filter-related errors with OData error format
Modified files:
odata-expression-parser/src/serializer.ts– Newodata-expression-parser/src/index.ts– Added exportodata-expression-parser/tests/serializer.test.ts– New (54 tests)ui/package.json– Added@reso-standards/odata-expression-parserdependencyui/Dockerfile– Copy parser into Docker buildui/src/utils/filter-sync.ts– Newui/src/pages/search-page.tsx– Lifted state, validationui/src/components/search-bar.tsx– Controlled component rewriteui/src/components/advanced-search.tsx– Two-way syncserver/src/odata/handlers.ts– 400 for invalid filters
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
91 |
@reso-standards/odata-expression-parser |
151 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
126 |
@reso-standards/certification-add-edit |
49 |
| Total | 589 |
v0.0.12 – 2026-03-04
$expand: Property Child Resources and Three FK Strategies
Extended $expand support from 3 resources (Media, OpenHouse, Showing) to all 7
Property child resources, and refactored navigation property discovery to support
three distinct foreign key strategies.
New child resources: PropertyRooms, PropertyGreenVerification,
PropertyPowerProduction, PropertyUnitTypes – all linked to Property via
ListingKey (direct FK strategy).
Three FK strategies in buildNavigationBindings:
- resource-record-key – polymorphic FK via
ResourceName+ResourceRecordKeycolumns (Media on any parent resource) - direct – child has the parent’s key field directly (OpenHouse, Showing,
PropertyRooms, PropertyGreenVerification, PropertyPowerProduction,
PropertyUnitTypes all have
ListingKey) - parent-fk – parent entity holds a FK to the target (to-one nav props like BuyerAgent, ListAgent, BuyerOffice, ListOffice on Property → Member/Office)
OpenAPI and EDMX metadata:
- OpenAPI: GET collection endpoints now include
$expandquery parameter with valid navigation property names listed in the enum - EDMX:
NavigationPropertyelements generated for all expansion fields with correctType(collection vs singleton) andPartnerattributes
Modified files:
router.ts– Three-strategybuildNavigationBindings, exported for testingopenapi-generator.ts–$expandquery parameter on GET collection endpointsedmx-generator.ts–NavigationPropertyelements from expansion metadatatypes.ts– ExtendedTARGET_RESOURCESwith 4 new child resources, addedFieldMetadata.isExpansionsupportpostgres-dal.ts– Updated expand query for direct FK strategymongo-dal.ts– Updated expand query for direct FK strategydata-access.ts– GeneralizedExpandBindingtype for all FK strategies
Data Generator: Property Child Resources
Added generators for 4 new child resources and made the seed plan dynamic.
property-child.ts– Generic generator for PropertyRooms, PropertyGreenVerification, PropertyPowerProduction, PropertyUnitTypesplan.ts–getRelatedResources()discovers valid child resources per parent using FK field analysis;getDefaultRelatedCount()returns sensible defaultsopen-house.ts,showing.ts– AddedListingKeyto generated records- Docker Compose seed scripts updated with all 7 child resources
UI: Data Generator Improvements
- Related Records section now shows only valid expansions for the selected parent resource (driven by server metadata, not hardcoded)
- Short display names for child resources (PropertyRooms → “Rooms”, etc.)
- Two-column responsive layout (resource/count on left, related records on right)
- Wider container (
max-w-5xl) for better use of horizontal space - Plan summary computes total related records dynamically
Modified files:
admin-client.ts– AddedRelatedResourceInfotype andrelatedResourcesfield toResourceStatusdata-generator.ts(server) – Status endpoint returnsrelatedResourcesper resource usinggetRelatedResources()from@reso-standards/data-generatordata-generator-page.tsx– Context-sensitive related records UI
nginx SPA Routing Fix
Fixed “Cannot GET /admin/data-generator” error on browser refresh. The nginx
config previously proxied all /admin/* paths to the backend. Split into a
dedicated /admin/ location block that proxies POST and /status to the API
while serving the SPA for page routes. Also added the 4 new child resources
to the OData entity and collection regex patterns.
Test Summary
17 new tests (navigation property discovery + EDMX generation).
| Package | Tests |
|---|---|
@reso-standards/validation |
91 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
126 |
@reso-standards/certification-add-edit |
49 |
| Total | 535 |
v0.0.11 – 2026-03-03
UI: Detail Page Layout and Field Grouping Improvements
Redesigned the detail page layout and improved field grouping behavior across all UI views.
Detail page layout (#15):
- Media carousel now sits on the right (half-width) with a summary pane on the left, side-by-side on desktop, stacked on mobile
- For Property: left pane shows formatted address, key, timestamp, and
configured summary fields (from
ui-config.json); grouped field sections appear below in collapsible panels - For resources without groupings: all fields display alphabetically in a two-column layout beside the carousel
Remove “Other” group for ungrouped resources (#14):
- Resources without field groupings (Member, Office, Media, OpenHouse, Showing) now display fields as a flat alphabetical list instead of wrapping them in a collapsible “Other” section
- Applies to detail page, record form, and advanced search
Fixed header/sidebar and request URI fix (#16):
- Header and left navigation sidebar now stay fixed; only the main content area scrolls
- Fixed “Request URI too large” error by omitting
$selectfor resources withsummaryFields: "__all__"(server returns all fields by default) - Increased nginx
large_client_header_buffersto4 32kfor complex OData queries
Modified files:
layout.tsx– Fixed header/sidebar with scrollable content areasearch-page.tsx– Omit$selectfor__all__resourcesnginx.conf.template– Increased header buffer limitdetail-page.tsx– Summary pane + carousel layout, summary field extractionrecord-form.tsx– Flat field grid when no groups existadvanced-search.tsx– Flat field rows when no groups exist
v0.0.10 – 2026-03-03
MongoDB Document Store Backend
Added MongoDB as a fully functional alternative database backend, selectable via
the DB_BACKEND environment variable.
New files:
filter-to-mongo.ts– OData$filterAST to MongoDB query translator with two modes: native query operators (index-friendly) for simple comparisons, and$expraggregation expressions for functions and arithmeticfilter-to-mongo.test.ts– 33 tests covering all comparison, logical, string, date, math, and arithmetic operatorsmongo-dal.ts– Production MongoDBDataAccessLayeradapter with cursor pagination and batch$inqueries for$expandresolutionmongo-init.ts– Collection and index setup (unique PK index per resource, compound FK index for child collections)
Modified files:
config.ts– AddedDB_BACKEND(postgres|mongodb) andMONGODB_URLenvironment variablesindex.ts– Conditional DAL instantiation via dynamic imports (mongodb package only loaded whenDB_BACKEND=mongodb)docker-compose.yml– Addedmongodbprofile withmongodb,server-mongo,ui-mongo, andseed-mongoservices
Deleted: mongo-dal.example.ts – superseded by production mongo-dal.ts
Docker usage:
- PostgreSQL (unchanged):
docker compose up -d - MongoDB:
docker compose --profile mongodb up -d mongodb server-mongo ui-mongo - MongoDB seed:
docker compose --profile seed-mongo up seed-mongo
Documentation
- Updated server README with MongoDB backend docs (env vars, project structure, DAL adapter comparison, filter translation, MongoDB-specific behavior)
- Updated reference-server README with Docker instructions for both backends
- Created READMEs for
@reso-standards/validation,@reso-standards/data-generator, and@reso-standards/certification-test-runner
v0.0.9 – 2026-03-03
Server-Driven Pagination with @odata.nextLink
Fixed $expand + pagination bug and added OData @odata.nextLink support for
server-driven infinite scroll.
Bug fix: When $expand=Media was used with $top/$skip, the PostgreSQL
adapter applied LIMIT/OFFSET to the flat LEFT JOIN result rows instead of
parent entities. With 50 Properties × 5 Media each, LIMIT 25 returned only 5
unique Properties after grouping, and COUNT(*) OVER() reported 250 instead of
50.
Fix: The PostgreSQL adapter now uses a CTE (WITH parent_page AS (...)) to
paginate parent rows first, then LEFT JOINs expanded navigation properties in
the outer query. COUNT and LIMIT/OFFSET apply to parent entities only.
@odata.nextLink: The collection handler now generates @odata.nextLink URLs
when more pages exist (i.e., result.value.length === $top). The nextLink
encodes $top/$skip along with all other query parameters ($filter, $select,
$orderby, $count, $expand).
UI: The useCollection hook now follows @odata.nextLink for infinite
scroll instead of manually computing $skip offsets. The fetchCollectionByUrl
client function fetches raw nextLink URLs.
DAL abstraction: The DataAccessLayer interface (data-access.ts) is
unchanged – the CTE logic is PostgreSQL-specific. The MongoDB adapter sketch
already handles this correctly (cursor pagination + batch expand). The nextLink
generation is OData protocol logic in the handler layer.
v0.0.8 – 2026-03-03
Required Address Fields
Enforced required address fields across all address-bearing resources. City, StateOrProvince, PostalCode, and Country (with resource-specific prefixes for Member and Office) must always be present.
- Validation: Added
requiredflag toFieldRuleinterface, with required address rules for Property, Member (MemberCity, etc.), and Office (OfficeCity, etc.) - Data generators: Property now always generates
StateOrProvinceandCountry; Office now generatesOfficeStateOrProvinceandOfficeCountry; Member now generates full address fields (MemberAddress1,MemberCity,MemberStateOrProvince,MemberPostalCode,MemberCountry)
Cross-Field Validation Rules
Added relationship constraints between fields in the Property resource:
- ListPrice >= ListPriceLow – when both are present, ListPrice must be greater than or equal to ListPriceLow
- BathroomsTotalInteger = sum of parts – when BathroomsTotalInteger and any bathroom part fields (BathroomsFull, BathroomsHalf, BathroomsPartial, BathroomsOneQuarter, BathroomsThreeQuarter) are present, the total must equal the sum of the parts
- New
CrossFieldRuleinterface with callback-based validators
Data Generator Consistency
- Bathroom fields generated parts-first (BathroomsFull, BathroomsHalf, etc.), then BathroomsTotalInteger computed as their sum
- ListPriceLow generated as 80–100% of ListPrice
Address Formatting Fix
Fixed USPS-format address separators in the UI:
- Added comma between City and StateOrProvince (was missing)
- Added space between StateOrProvince and PostalCode (was missing)
- Fixed StreetDirSuffix separator (was comma, should be space – it is part of the street line)
- Addresses now render correctly:
8653 Main Blvd, Salem, OR 45241
Summary Display
- Summary cards now show all configured fields in fixed order, displaying ` – ` for missing values instead of hiding empty fields
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
91 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
76 |
@reso-standards/certification-add-edit |
49 |
| Total | 485 |
v0.0.7 – 2026-03-03
Business Rules Validation
Added field-specific business rules to the @reso-standards/validation package in a new
business-rules/ subfolder. Rules are enforced on both the server API (POST/PATCH)
and the UI input forms.
- Price fields (ListPrice, OriginalListPrice, PreviousListPrice, ClosePrice, ListPriceLow): must be between 0 and 1,000,000,000
- Bedroom fields (BedroomsTotal, BedroomsPossible, MainLevelBedrooms): must be between 0 and 100
- Bathroom fields (BathroomsTotalInteger, BathroomsFull, BathroomsHalf, BathroomsOneQuarter, BathroomsPartial, BathroomsThreeQuarter, MainLevelBathrooms): must be between 0 and 100
- Rule registry pattern (
getBusinessRules(resourceName)) extensible to other resources - Integrated into
validateRecordwith deduplication – fields that already have type-level failures skip business rule checks - 24 new tests in
business-rules.test.ts
Locale-Aware Formatting
Added formatFieldValue utility in the UI for locale-aware display formatting:
- Currency fields formatted as USD with
Intl.NumberFormat(e.g.,$4,034,663.58) - Integer fields formatted with thousands separators (e.g.,
1,234) - Decimal fields formatted with up to 2 fraction digits
- DateTimeOffset fields formatted with
toLocaleString() - Booleans displayed as
Yes/No - Handles both numeric and string representations from the OData API (Edm.Decimal values are serialized as JSON strings per OData 4.01 spec)
Address Display
- Summary cards: Property records show a single composed USPS-format address line
(e.g.,
123 Main St, Springfield, IL 60601) instead of individual address fields - Detail pages: formatted address displayed in the pinned header section
formatAddressassembles addresses from StreetNumber, StreetDirPrefix, StreetName, StreetSuffix, StreetDirSuffix, City, StateOrProvince, PostalCode; falls back to UnparsedAddress if structured fields are empty
Nginx SPA Routing Fix
Fixed browser refresh on SPA routes (e.g., /Property) returning raw JSON instead of
the React app. The nginx config now uses the Accept header to distinguish API requests
(application/json) from browser navigation (text/html), using the error_page 418
- named location pattern for reliable proxying.
PostgreSQL Schema: TEXT instead of VARCHAR
Changed the schema generator to use TEXT for all Edm.String columns instead of
VARCHAR(n). PostgreSQL stores TEXT and VARCHAR identically, but TEXT columns benefit
from TOAST for large values.
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
65 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
76 |
@reso-standards/certification-add-edit |
49 |
| Total | 459 |
v0.0.6 – 2026-03-03
PostgreSQL Schema: TEXT instead of VARCHAR
Changed the PostgreSQL schema generator to use TEXT for all Edm.String columns
instead of VARCHAR(n). PostgreSQL stores TEXT and VARCHAR identically, but TEXT
columns benefit from TOAST (Transparent Oversized-Attribute Storage), which moves
large values out-of-line and reduces the fixed-width row portion. This resolves
“row is too big” errors when populating resources with many fields (e.g., DD 2.0
Property has 652 fields).
Data Dictionary 2.0 Metadata
Updated server metadata from DD 1.7 to DD 2.0 (1,727 fields, 3,256 lookups, 41 resources vs 1,316 fields, 2,951 lookups in 1.7).
Data Generator Fixes
- Decimal fields now respect RESO metadata
precision/scaleconstraints (e.g., NUMERIC(5,2) capped at 999.99 instead of 10,000) - All date/time fields use ISO 8601 format (no bare time strings)
- Nullable fields randomly populated at 60% fill rate for realistic sparse records
- Docker fixes: multi-dependency builds, Alpine wget healthcheck, nginx admin/oauth routes
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
41 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
71 |
@reso-standards/reference-server |
76 |
@reso-standards/certification-add-edit |
49 |
| Total | 435 |
v0.0.5 – 2026-03-03
Data Generator: @reso-standards/data-generator
New standalone package at tools/data-generator/ that generates realistic RESO Data
Dictionary records for seeding OData servers with test data.
Three Output Modes
- HTTP – POSTs records directly to a running OData server via the Add/Edit API
- JSON – Writes records as JSON files to a directory structure (
<resource>/<key>.json) - curl – Generates a
seed.shscript with curl commands for later execution
Resource Generators
Six resource-specific generators produce realistic field values:
- Property – addresses, pricing ($50k–$10M), bedrooms, bathrooms, coordinates (US bounds), public remarks
- Member – first/last name pools, email patterns, phone numbers, designations
- Office – brokerage names, office phones, addresses, national association IDs
- Media – image URLs, sequential ordering, MIME types, linked to parent via ResourceName/ResourceRecordKey
- OpenHouse – future-dated events with weekend preference, time ranges, linked to parent Property
- Showing – future-dated appointments with time ranges, instructions, linked to parent Property
A generic field generator handles all Edm types (String, Boolean, Int16/32/64, Decimal, Date, DateTimeOffset, TimeOfDay, Guid) and enum/collection lookups from metadata.
CLI
Interactive mode with @inquirer/prompts:
npx reso-data-generator
Non-interactive mode with flags:
npx reso-data-generator -r Property -n 50 -f json -o ./seed-data \
--related Media:5,OpenHouse:2,Showing:2 -t admin-token
69 tests across 4 test files
Auth System for Reference Server
Added role-based authentication to @reso-standards/reference-server.
- Three roles:
read,write,adminwith hierarchy (admin > write > read) - Static tokens from environment variables (
ADMIN_TOKEN,WRITE_TOKEN,READ_TOKEN) with development defaults - Dynamic tokens issued by the mock OAuth2 endpoint with optional
rolequery parameter - Express middleware (
requireAuth) – 401 for missing/invalid token, 403 for insufficient role - Backward compatible – auth is optional by default (
AUTH_REQUIRED=false) so existing workflows continue without tokens - 9 auth tests
Admin API and UI
Server: Admin Data Generator Endpoint
POST /admin/data-generator– generates records using the DAL directly (requires admin role)GET /admin/data-generator/status– returns available resources with field and record counts- Admin router mounted at
/adminwith admin auth middleware
UI: Data Generator Page
- Resource selector dropdown with field and record counts
- Record count input (1–10,000)
- Related record checkboxes with per-resource count inputs
- Generation plan summary showing total records to be created
- Progress indicator and results display (created/failed/duration)
- Amber-themed admin section with auth token input
Seed Script and Docker
seed.sh– executable bash script that waits for server health, then seeds 50 Properties (with Media, OpenHouse, Showing), 20 Members, and 10 Offices- Docker Compose updated with server healthcheck and optional seed service
(
docker-compose --profile seed up)
Root Tooling
- Lefthook pre-commit hooks updated with
typecheck-data-generatorandtest-data-generator - Root
package.jsonupdated withtest:data-generatorscript
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
41 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/data-generator |
69 |
@reso-standards/reference-server |
76 |
@reso-standards/certification-add-edit |
49 |
| Total | 433 |
v0.0.4 – 2026-03-03
Certification Test Runner: @reso-standards/certification-test-runner
Extracted generic OData certification test infrastructure from certification/add-edit
into a reusable package at tools/certification/test-runner/. Future certification
modules (e.g., read-only, search) can import the shared framework instead of
duplicating test logic.
- OData protocol validators – status codes, headers (OData-Version, Location, EntityId, Preference-Applied), response body validators (JSON, annotations, etag, error format), payload echo checks
- Reporter – console (human-readable) and JSON output formats
- HTTP client – OData request wrapper delegating to
@reso-standards/reso-client - Auth helpers – bearer token and OAuth2 Client Credentials resolution
- Metadata helpers – CSDL parsing, entity type lookup, payload validation
against metadata using
@reso-standards/validation - Generic test helpers – primary key extraction, schema assertions, scenario result building
Validation Integration in Certification
certification/add-edit now uses @reso-standards/validation for metadata-driven field
validation. The validatePayloadAgainstMetadata() function performs full type
checking (unknown fields, Edm type mismatches, negative numerics, MaxLength,
integer enforcement, collection/enum checks) via the shared validation package.
The certification scenario pre-flight check (makeSchemaAssertion) validates
field existence only – value validation is the server’s responsibility and is
exercised by the failure test scenarios.
Package Restructure
certification/add-editlib files are now thin re-exports from@reso-standards/certification-test-runner, keeping only add-edit-specific scenario implementations (8 scenarios), types (PayloadSet, DeletePayload, ScenarioName), mock server, and CLI- Updated build order:
test-runnerbuilds beforeadd-edit - Lefthook pre-commit hooks updated with
typecheck-test-runnerat priority 2
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
41 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/reference-server |
67 |
@reso-standards/certification-add-edit |
49 |
| Total | 355 |
v0.0.3 – 2026-03-03
Shared Validation Package: @reso-standards/validation
Extracted field validation logic into a standalone isomorphic package at
tools/validation/. Both the reference server API and the React UI now import
from this single source of truth.
- Isomorphic – no Node.js or browser APIs; works in any JS runtime
- Metadata-driven field validation: unknown field detection, type checking for all Edm types, negative number rejection, MaxLength enforcement, integer-only enforcement for Int types, collection and enum validation
- Subpath exports –
@reso-standards/validation(top-level barrel) and@reso-standards/validation/metadata(direct subpath) for future extensibility - 41 tests –
tests/validate.test.ts // TODO: Add executable business rules validationplaceholder for future grammar-based rules engine
Reference Server UI
Added a React UI for the reference server at tools/reso-reference-server/ui/.
- Tailwind CSS v4, React Router v7, Vite 6 dev server with proxy
- Resource browser with infinite scroll pagination (
$top/$skip) - Detail pages with fields grouped by RESO Data Dictionary “Groups” categories
- Media carousel with mock placeholder images
- Dynamic Add/Edit forms generated from RESO field metadata
- Advanced search with grouped field filters that build OData
$filterexpressions - Delete confirmation with key prompt
- Dark mode toggle (system preference + manual override)
- Client-side validation using
@reso-standards/validationwith per-field error display that clears as fields are corrected
Improved Error Messages
- Server API – OData error responses now include
target(operation name) at the top level, and per-field errors use human-friendly messages withtarget(field name) andmessageproperties in thedetailsarray - UI – Fixed client-server error mapping (
target/messageinstead offield/reason), added submit error banner with auto-clear
Test Summary
| Package | Tests |
|---|---|
@reso-standards/validation |
41 |
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/reference-server |
67 |
@reso-standards/certification-add-edit |
49 |
| Total | 355 |
v0.0.2 – 2026-03-02
Developer Tooling: Biome + Lefthook
Added pre-commit hooks and a shared linter/formatter to enforce code quality across all packages.
- Biome for linting and formatting – configured to match the RESO certification-utils style (single quotes, semicolons, no trailing commas, 140 char line width, LF line endings, arrow parens avoided) – biome.json
- Lefthook for git pre-commit hooks – lefthook.yml
- Root
package.jsonwithlint,lint:fix, andtestconvenience scripts – package.json
Pre-commit Hook Flow
- Lint + auto-fix – Biome checks staged
.tsfiles, auto-fixes formatting and lint issues, and re-stages the fixed files - Type check –
tsc --noEmitin all 4 packages, respecting the build dependency order (filter-parser first, then client/server/test-tool) - Tests –
vitest runin all 4 packages (314 tests)
Codebase Reformatted
All 79 TypeScript source files were reformatted to the RESO standard style. All 314 tests pass after reformatting.
Setup
npm install # installs biome + lefthook
npx lefthook install # activates git hooks
v0.0.1 – 2026-03-02
Initial release of the RESO Transport tooling suite. Introduces four interconnected packages for building, testing, and validating OData 4.01 services that conform to the RESO Data Dictionary and Web API specifications.
New Packages
@reso-standards/odata-expression-parser – odata-expression-parser/
Standalone, zero-dependency library for parsing OData $filter expressions into a
typed AST. Shared by both the client SDK (query validation) and the reference server
(SQL translation).
- 8 comparison operators:
eq,ne,gt,ge,lt,le,has,in - 6 arithmetic operators:
add,sub,mul,div,mod,divby - 28+ built-in functions: string (
contains,startswith,endswith,tolower,toupper,trim,concat,length,indexof,substring,matchesPattern), date/time (year,month,day,hour,minute,second,fractionalseconds,now,date,time,maxdatetime,mindatetime,totaloffsetminutes,totalseconds), math (round,floor,ceiling), type (cast,isof) - Lambda operators:
anyandallwith variable binding - Literal types: string, number, boolean, null, date, datetimeoffset, timeofday, duration, guid, enum
- 97 tests – tests/filter-parser.test.ts
@reso-standards/reso-client – reso-client/
OData 4.01 client SDK for TypeScript, inspired by Apache Olingo. Provides URI building, CRUD helpers, CSDL metadata parsing/validation, query option validation, and response parsing.
- URI Builder with compound keys,
$filter,$select,$orderby,$top,$skip,$count,$expand,$search,$compute,$format– src/uri/builder.ts - CSDL/EDMX Parser supporting EntityType, ComplexType, EnumType, NavigationProperty (with ReferentialConstraint, Partner, ContainsTarget), Action, Function, Singleton, EntityContainer, inheritance (BaseType/Abstract), OpenType, HasStream, IsFlags – src/csdl/parser.ts
- CSDL Validator for structural correctness – src/csdl/validator.ts
- HTTP Client with OAuth2 Client Credentials and bearer token auth – src/http/client.ts
- CRUD Helpers:
createEntity,readEntity,updateEntity(PATCH),replaceEntity(PUT),deleteEntitywith If-Match/If-None-Match ETag support – src/crud/ - Query Validator checking
$filter,$select,$orderby,$expandagainst CSDL metadata – src/query/validator.ts - Response Parser with
@odata.nextLinkauto-paging (followAllPages), annotation extraction, and OData error parsing – src/response/parser.ts - Metadata Fetcher (
fetchRawMetadata,fetchAndParseMetadata) – src/metadata/fetcher.ts - 101 tests across 5 test files
- 5 runnable examples:
@reso-standards/reference-server – reso-reference-server/
Metadata-driven OData 4.01 reference server for the RESO Data Dictionary. Reads RESO JSON metadata and dynamically generates PostgreSQL tables, OData CRUD endpoints, EDMX metadata, and OpenAPI documentation.
- Data Access Layer interface abstracting persistence from query handling
–
server/src/db/data-access.ts - PostgreSQL implementation using LEFT JOIN + app-side grouping for
$expand–server/src/db/postgres-dal.ts - MongoDB example demonstrating batch-query pattern for document stores
–
server/src/db/mongo-dal.example.ts $filter→ SQL translation using@reso-standards/odata-expression-parserAST with parameterized queries (SQL injection safe) –server/src/db/filter-to-sql.ts- Collection GET handler with
$filter,$select,$orderby,$top,$skip,$count,$expandsupport –server/src/odata/handlers.ts - Navigation property auto-detection via RESO
ResourceName/ResourceRecordKeyFK convention –server/src/odata/router.ts - 6 target resources: Property, Member, Office, Media, OpenHouse, Showing (1,316 fields, 2,951 lookup values)
- 67 tests across 5 test files
@reso-standards/certification-add-edit – certification/add-edit/
RESO Web API Add/Edit Endorsement (RCP-010) compliance testing tool. Sends known-good and known-bad JSON payloads to OData servers and validates responses against 8 Gherkin BDD certification scenarios.
- Refactored to use
@reso-standards/reso-clientfor HTTP, authentication, and CSDL metadata parsing (previously used rawfetchandfast-xml-parserdirectly) - 49 tests across 4 test files – all passing after refactoring
Cross-Package Architecture
validation (zero deps)
├──> reso-reference-server (depends on validation + filter-parser)
└──> reso-reference-server/ui (depends on validation)
odata-expression-parser (zero deps)
├──> reso-client (depends on filter-parser)
│ └──> certification/add-edit (depends on reso-client)
└──> reso-reference-server (depends on filter-parser)
- The shared filter parser is used by the client SDK for query validation and by
the server for SQL WHERE clause generation – ensuring consistent
$filterbehavior - The data access layer abstraction allows swapping persistence backends (Postgres, MongoDB, in-memory) without changing handler logic
- The test tool refactoring removes duplicated HTTP and metadata parsing code in favor of the shared client SDK
OData 4.01 Spec Compliance
Implementation was audited against three OData 4.01 specification documents:
All high and medium priority gaps were addressed. Remaining lower-priority items are tracked in TODO.md.
Test Summary
| Package | Tests |
|---|---|
@reso-standards/odata-expression-parser |
97 |
@reso-standards/reso-client |
101 |
@reso-standards/reference-server |
67 |
@reso-standards/certification-add-edit |
49 |
| Total | 314 |