Skip to content

Changelog

All notable changes to Filament Address Pro will be documented in this file.

The format follows Keep a Changelog, and this project adheres to Semantic Versioning.

[1.0.75-beta] - 2026-06-02

Fixed

  • Docs: USER-GUIDE.md broken links. The README overview link was labelled "README" (which 404s on the docs site) — changed to "Overview". A relative link to ../config/addresses.php pointed at the raw config file rather than the configuration guide — changed to CONFIGURATION-GUIDE.md.

[1.0.74-beta] - 2026-06-02

Added

  • Docs: USER-GUIDE.md added to the docs site. The file was incorrectly excluded as an internal document; it is a 1,152-line user-facing guide and now appears in the docs site sidebar under the Filament Address Pro group after Getting Started.

[1.0.73-beta] - 2026-06-02

Fixed

  • Docs: VitePress URL casing. Reverted to uppercase URL destinations — VitePress only rewrites .md-extension links; bare relative links pass through as-is, so destination paths must match source filename case exactly.
  • Docs: GitHub-compatible anchor slugify. VitePress default collapses & to a single hyphen, but the docs' manually authored tables of contents use the GitHub style (double hyphen). A custom slugify function now matches the existing anchors.

[1.0.71-beta] - 2026-06-01

Added

  • VitePress documentation site live. docs.viewflex.net/filament-address-pro/ is now live on Cloudflare Pages, built from the package repo main branch. Includes all 19 published docs with uppercase URL paths (matching source filenames) and local full-text search. A "View Docs" button has been added to the viewflex marketing page hero.

[1.0.70-beta] - 2026-05-31

Added

  • morphMap support in import and export. The importer now resolves morphMap aliases to class names via Relation::getMorphedModel() before the entity whitelist check and ::find() call — apps that register short aliases (e.g. 'user' => User::class) work transparently. The exporter always emits the resolved class name in the entity_type column so exported CSVs round-trip correctly regardless of whether the target instance has the same morphMap configuration. No change required for apps that do not use morphMap.

[1.0.69-beta] - 2026-05-31

Added

  • Export: subdivision ID fields for round-trip imports. Both export dialogs (Export Results and Export Addresses) now offer administrative_area_id, locality_id, and dependent_locality_id as selectable fields, labelled "for re-import". These are the stable subdivision ULIDs from the package's seed data — identical across all instances of the same package version. Including them in an export makes re-importing that CSV lossless: subdivision matching is exact rather than fuzzy. Not selected by default.

Improved

  • Import: addressable_type / addressable_id accepted as column name aliases. The standalone importer now recognizes addressable_type and addressable_id (the underlying model column names) as aliases for entity_type and entity_id. A CSV exported directly from the package can be re-imported without any column renaming.

  • Import: ULID-first subdivision matching. When administrative_area_id, locality_id, or dependent_locality_id columns are present in the import file, the importer validates the ULID against the reference data and uses it directly — skipping fuzzy text matching for that level. If the ULID is not found (e.g. after a version upgrade that reorganized the subdivision data), the import falls back to fuzzy matching on the text column automatically. Combined with the new export fields, this makes export → re-import a fully lossless round-trip.

  • Export dialog field ordering. Subdivision fields now appear in order of decreasing geographic scope: State/Province → City → District → IDs, in both export dialogs.

  • IMPORT-EXPORT.md overhauled. Added: round-trip import workflow; firstOrCreate/lookup-by-email building block example with a decision table for choosing standalone vs. building blocks; addressable_* column aliases; subdivision ID columns. Fixed: dependent_locality was absent from the example CSV, field normalization examples, building block code samples, and the Export section (which listed incorrect field names and was missing several fields). Updated last-modified date.

[1.0.68-beta] - 2026-05-28

Fixed

  • Venue-name-in-line-1 pattern (variant): non-numeric venue names with a different geocoded street. The original guard only detected the pattern when geocodedLine1 === originalLine2 (e.g. Palais des Congrès). A second variant now fires when geocodedLine1 ≠ originalLine1, originalLine1 has no leading digit (confirming it is a venue/establishment name rather than a correctable street), and originalLine2 is non-empty. Example: "Wembley Arena / Arena Square" where Google geocodes to "Engineers Way". Both original lines are preserved and _venue_name_pattern is set so applyVerification() suppresses spurious standardization changes. 2 new tests.

  • GB (and similar): verification provider returns county/historic-county (L2) instead of nation-level subdivision (L1). Smarty International returns counties such as "Middlesex" or "Somerset" for GB addresses, but GB's administrative_area_level is 1 (England / Scotland / Wales / Northern Ireland). applyVerification() now checks both the provider value and the geocoded value against the country's L1 subdivision options via fuzzyMatchSubdivision; if the geocoded value matches L1 and the provider value does not, the geocoded state is preserved. CN and other countries where neither value matches are unaffected. 1 new test.

  • APPROXIMATE geocoding: wrong street proposed as standardized address_line_1. Google's APPROXIMATE location type resolves only to postal-code area, not street level; the returned route component may be a nearby street unrelated to the address (e.g. "Magdalene Street" for "4 Pennine Way, Glastonbury BA6 9EH"). processFromAddress() now restores the original address_line_1 when location_type is APPROXIMATE and the geocoded street differs from the input. 2 new tests.

  • KR/POI: Korean-only address_line_1 when Google Places omits street_address. processFromPlace() now parses the first comma-segment of formatted_address when street_address is empty and that segment starts with a digit (e.g. "110 Buljeong-ro" from "110 Buljeong-ro, Bundang-gu, Seongnam-si, Gyeonggi-do, South Korea"). This avoids falling back to the place name, which is often in the local script only (e.g. "분당도서관"). has_street_address is updated accordingly so needs_review is not raised unnecessarily. 2 new tests.

  • KR/POI: district (gu) not populated when reverse geocode omits sublocality. processFromPlace() now recovers dependent_locality from formatted_address when the reverse geocode returns no sublocality components. The segment immediately before the city in formatted_address is used as the district candidate (e.g. "Bundang-gu" from "…, Bundang-gu, Seongnam-si, …"). The form's fuzzy-matcher then resolves this to the correct DB subdivision. Applies to any country with a three-level "STREET, DISTRICT, CITY, …" formatted_address structure. 2 new tests.

[1.0.65-beta] - 2026-05-27

Fixed

  • Smarty widget: disabled section still appeared in status message subhead. When us_enabled or international_enabled is false, the subhead (e.g. "Healthy: 1,000 US · 857 Intl remaining.") was still including the hidden section. Both the none and healthy branches now respect the show flag. 2 new tests.

[1.0.64-beta] - 2026-05-27

Fixed

  • CN/JP/KR autocomplete: street number dropped when Places API omits street_number component. The address search field now captures the raw autocomplete input at selection time and recovers the leading street number (bare digit or No.NNN prefix format) when addressComponents lacks a street_number entry. This handles both "107 Suzhou Rd" and "No.1088 Qianshan Rd" formats.
  • CN/JP/KR autocomplete: city and district not matched when geocoder omits administrative_area_level_1. Google's geocoder sometimes returns a city without a province/state for CJK addresses. Added CountryService::inferStateFromCity() which fuzzy-matches the city against all locality-level subdivisions for the country and walks up the hierarchy to infer the parent state. Both the verify-button and autocomplete paths in the address form now use this fallback, allowing Hefei Shi → Anhui and similar mappings to resolve correctly.

[1.0.63-beta] - 2026-05-24

Fixed

  • CI test failures after street-number sanity check. The smartySuccessResponse() test helper used a generic address1: "1 Test Street" response for all countries. After the new sanity check, tests that sent multi-digit street numbers (GB "10 Downing Street", CA "123 Main St", IL "Ranak St 19") failed because the mock response didn't contain the original number. Updated smartySuccessResponse() to accept an optional address1 parameter; routing tests now pass a matching value.

[1.0.62-beta] - 2026-05-24

Fixed

  • Smarty International: garbled address1 response shown in standardization dialog. When Smarty's International API returns an address1 that does not contain the street number from the original input (e.g. "Street Hilton" for input "205 HaYarkon Street"), the response is treated as a verification failure rather than offered as a standardized suggestion. The check applies only to multi-digit street numbers (≥ 2 digits) to avoid false positives from single-digit numbers which can match as substrings.

[1.0.61-beta] - 2026-05-24

Fixed

  • Smarty International: establishment name appended to address_line_1. Smarty's International Street API sometimes packs a truncated establishment/premise name onto the end of address1 (e.g. "115 Hayarkon Street Sheraton Grand") while address2 holds the full name ("Sheraton Grand Tel Aviv"). The standardization dialog would show the hotel name in both fields. Fixed by detecting word-boundary overlap between the tail of address_line_1 and the head of address2 (or the premise component when address2 was cleared by the existing locality filter). A minimum overlap of 2 words is required to avoid false positives from single-word coincidences.

[1.0.60-beta] - 2026-05-24

Fixed

  • Verification badge popover not closing on scroll when page has a fixed header. The scroll-close listener was registered on window, which only fires when the viewport itself scrolls. On pages with a fixed header and a scrollable inner content area, scroll events fire on the inner container — not on window. Changed to document.addEventListener('scroll', ..., true) (capture phase), which intercepts scroll events from any element in the document tree.

[1.0.59-beta] - 2026-05-24

Fixed

  • Verification badge popover scroll-close and outside-click-close not working on create/demo forms. The scroll listener and outside-click listener that dismiss the popover lived in the document-level $docClickScript, which is never registered on create forms (badge not in DOM on initial load). Scroll-close is now handled by a capture-phase scroll listener on window registered inline in the ontoggle handler and removed on close. Outside-click-close is now handled by a full-screen transparent backdrop <div> (position:fixed; inset:0; z-index:-1) inside the popover that catches any click outside the visible card. Both work without document-level listeners.

[1.0.58-beta] - 2026-05-24

Fixed

  • Verification badge popover not opening on create forms. The document-level toggle listener that positions the popover was never registered when the badge first appeared during a Livewire re-render — Livewire's morphdom does not re-execute <script> tags added dynamically. On create forms the badge is not in the DOM on initial page load, so the script never ran and clicking the badge had no visible effect (cursor changed but popover did not open). Fixed by moving positioning to an ontoggle inline handler on <details> and Re-verify delegation to an inline onclick on the button. Both work regardless of whether the document listener was registered. The document-level script is now a progressive enhancement for outside-click-close and scroll-close only.

[1.0.57-beta] - 2026-05-22

Added

  • Verification badge popover with Re-verify button (FAP-150). The green "✓ Verified by {Provider}" and blue "✓ Accepted by user" badges are now clickable. Clicking opens a small popover showing the provider name and verification date. A Re-verify Address button inside the popover re-triggers the existing verify action — giving staff a clear Re-verify entry point without cluttering the form with a permanently-visible button on already-verified addresses. The popover is absent when Re-verify is not available (provider not configured, country not covered, no address entered). Implemented with native <details>/<summary> for the toggle (no JavaScript onclick), position:fixed to escape Filament's overflow:hidden TextEntry wrapper, and a document-level delegation listener that survives Livewire re-renders.

[1.0.56-beta] - 2026-05-21

Added

  • Geocoding-as-verification fallback for countries with no postal provider (FAP-151). When processFromAddress() runs and the configured postal provider does not cover the address country (e.g. CN, most of Africa/Central Asia), a ROOFTOP or RANGE_INTERPOLATED geocoding result is now treated as verified: is_verified = true, verification_provider = 'google_geocoding'. GEOMETRIC_CENTER and APPROXIMATE results are not promoted — they remain unverified and continue to set needs_review = true. The form badge shows "✓ Verified by Google Geocoding". No new API calls required — geocoding already runs for coordinates.

  • Quality scorer: neutral score for addresses in countries with no provider coverage (FAP-152). AddressQualityScorer no longer penalizes unverified addresses when no postal provider covers the country. When providers are configured but none support the address country, calculateVerificationScore() returns verified_basic (20 pts) instead of 0 — neither a bonus nor a penalty. The guard requires at least one provider to be configured globally; environments with no API keys (dev/test/provider=none) are unaffected and score 0 as before.

[1.0.55-beta] - 2026-05-21

Added

  • Chunked processing with per-chunk transactions for bulk operations (FAP-155). BulkVerifyAddressesJob now processes addresses in chunks of 50, each wrapped in a DB::transaction(). A failed chunk is logged and skipped — remaining chunks continue. Stats are aggregated across chunks. Inner retry with exponential backoff (3 attempts) handles transient failures before giving up on a chunk.

  • Queued jobs for import and quality recalculation (FAP-153). Large imports (> 50 rows or with Geocode/Verify enabled) are dispatched as ImportAddressesJob background jobs with chunked processing, per-chunk transactions, and automatic retry. Quality recalculation dispatches RecalculateQualityJob (using chunkById(200)) with scope support (all, unscored, below_threshold, selected). All jobs include progress tracking, completion notifications, and sync-driver detection for inline execution.

  • RetriesChunks trait — shared per-chunk retry logic (3 attempts, exponential backoff) used by all bulk job types.

Fixed

  • notifications table missing on fresh installs causes panel errors. AddressingPanelProvider enables ->databaseNotifications() (added in v1.0.52-beta), which causes Filament to query the notifications table on every page load. Apps that have never run php artisan notifications:table hit a fatal DB error immediately. Installation docs now include php artisan notifications:table as a required setup step.

[1.0.54-beta] - 2026-05-20

Fixed

  • administrative_area false change on Verify for GB (and other countries where Smarty returns a finer-grained subdivision). After verification, the post-verification geocoding result is now used to override state/administrative_area — Google's administrative_area_level_1 aligns with the DB hierarchy (e.g. "England" for GB, full state names for US), while Smarty International sometimes returns a postal county or region (e.g. "London") that would appear as a spurious suggested change in the dialog.
  • Accept Address button re-triggers the review dialog on next open. The _review_initialized hydration check reads needs_review from the verification_metadata JSON blob. The Accept handler cleared the indexed needs_review column but left the flag in the blob, causing the standardization dialog to re-appear every time the record was reopened after accepting. The blob is now cleaned up (removes needs_review, suggested_changes, flagged_at) to match the cleanup already done by the Use Standardized path.
  • address_line_2 duplication when provider absorbs unit/level into address_line_1. Smarty International for AU/NZ returns the secondary address in slash notation (U 503/17 Help St, L 32 2 Chifley Sq). The original address_line_2 (Unit 503, Level 32) was still in components, causing both lines to show the unit in the standardization dialog. After applying the provider's address lines, the secondary prefix is stripped and the remaining token is checked as a standalone word in the new address_line_1; if found, address_line_2 is cleared. Guard is skipped for JP/CN/KR romanized/custom-extractor paths.

[1.0.53-beta] - 2026-05-18

Fixed

  • needs_review badge now explains USPS DPV secondary address flags. When USPS returns DPV S (unit type mismatch) or D (missing secondary), the badge showed generic "⚠ Needs Review" with no context. The review_reason stored in verification_metadata is now shown in the badge (e.g. "⚠ Needs Review — Address confirmed but type does not match USPS records").

[1.0.52-beta] - 2026-05-18

Fixed

  • AddressingPanelProvider now enables database notifications. The bulk verify job sends a completion notification via ->sendToDatabase(), but the panel was missing ->databaseNotifications() — the bell icon never appeared. Added to match the behaviour of the bundled fila dev-app panel.

[1.0.51-beta] - 2026-05-18

Fixed

  • needs_review form badge now shows provider rejection reason. When Verify Address fails (e.g. Ambiguous / Address Not Found), the reason was only shown in the ephemeral toast — once it faded the badge reverted to generic "⚠ Needs Review" with no context. The verification error is now stored in metadata and displayed in the badge as "⚠ Not verified — Multiple addresses matched" (first clause only; the instructional tail stays in the toast).

[1.0.50-beta] - 2026-05-18

Fixed

  • needs_review form badge now explains why review is needed. The amber badge in the edit form previously always showed "⚠ Needs Review" with no context. It now distinguishes: suggested_changes present → "⚠ Needs Review — suggested changes available"; Smarty Partial → "⚠ Partially verified (Smarty) — street-level only"; other → unchanged fallback. Mirrors the context already shown in the listing table tooltip.

[1.0.49-beta] - 2026-05-18

Fixed

  • Accept Address after verification failure now produces an "Accepted" badge. When a provider returns Ambiguous or Address Not Found, clicking Verify then Accept left the record as "Not Verified" — the Accept button only cleared needs_review but never set is_verified. Now, accepting an unverified address sets is_verified=true, verified_at, and manually_accepted=true, producing the blue "Accepted by user" badge in both the form and the listing table. The existing needs_review Accept path (Smarty Partial, suggested changes) is unchanged — those records are already is_verified=true.

[1.0.48-beta] - 2026-05-18

Fixed

  • address_line_2 fallback creates duplicate when venue name resolves to same street. When a caller passes a venue name in address_line_1 and the real street in address_line_2 (e.g. Moscone Center / 747 Howard Street), geocoding correctly resolves address_line_1 to the street — but the v1.0.46 fallback then copied address_line_2 anyway, sending 747 Howard Street 747 Howard Street to USPS and causing a 400 Address Not Found. Fix: skip the fallback when address_line_2 equals the geocoded address_line_1 (case-insensitive).
  • DefaultAddressExtractor uses wrong street order for German and other name-first countries. German (and Austrian, Swiss, Dutch, Nordic, Belgian) addresses follow the convention route number (Werner-Heisenberg-Allee 25), not number route (25 Werner-Heisenberg-Allee). The extractor was always using Western number-first order. Added NUMBER_AFTER_ROUTE_COUNTRIES constant (DE, AT, CH, NL, BE, NO, SE, DK, FI) and country-aware assembly.
  • needs_review badge tooltip shows provider name in lowercase. Provider is stored as smarty in the database; tooltip now uses ucfirst() to display Smarty.

[1.0.47-beta] - 2026-05-17

Fixed

  • needs_review badge tooltip misleading for Smarty Partial results. The tooltip always said "has suggested changes that need review" regardless of why the flag was set. When needs_review is set due to a Smarty Partial verification (no delivery-point precision), there are no suggested_changes in the metadata — no dialog to open, nothing to diff. The tooltip now distinguishes: suggested changes present → original wording; Partial verification → "Address was only partially verified (provider). Open to accept or re-verify."; other → generic fallback.

[1.0.46-beta] - 2026-05-17

Fixed

  • address_line_2 lost during geocoding + verification flow. Google geocoding never returns suite/unit numbers as a separate address component. When processFromAddress() passed geocoded components to the verification provider, the original address_line_2 (e.g. "Suite 7500") was silently dropped. Verification providers (USPS, Smarty) then received only the street address and could not validate or return the secondary unit. Fix: preserve the caller's original address_line_2 when geocoding returns nothing for that field — same pattern as the existing address_line_1 fallback.

[1.0.45-beta] - 2026-05-17

Fixed

  • Export Results button renders with dark text. Filament v4's WCAG contrast algorithm selects dark text on success (green) in light mode. Added fi-btn-export-results-addresses CSS class with pinned --bg/--text variables to force emerald-600 background with white text in both light and dark modes, matching the Import Addresses button.

[1.0.44-beta] - 2026-05-17

Fixed

  • ExportResultsAddressesAction class not found error. Used Filament\Tables\Actions\Action instead of Filament\Actions\Action — the correct namespace for toolbar actions in Filament v4.

[1.0.43-beta] - 2026-05-17

Added

  • Export Results toolbar action. New ExportResultsAddressesAction added to the addresses table toolbar. Exports all records matching the current filters, search, and sort — not just selected records. Supports CSV and XLSX formats with the same field selection as the bulk export action. Respects the export gate for visibility and authorization.

[1.0.42-beta] - 2026-05-17

Fixed

  • needs_review not propagated on blur geocoding no-changes path. When blur geocoding ran verification and no field changes were detected, the form hardcoded $set('needs_review', false) instead of reading from $components['needs_review']. A Smarty Partial result on this path left needs_review=false in the DB despite the provider signalling otherwise.
  • needs_review not propagated on map selection (reverse geocoding) success path. The coordinates/map selection handler set is_verified, verification_provider, verified_at, and verification_metadata from components but never set needs_review. A Partial verification result from this path was silently dropped.
  • BulkVerificationService::updateAddress() hardcoded needs_review=false. When bulk verification accepted an address with minor/no changes, updateAddress() unconditionally cleared the needs_review flag regardless of what the provider returned. Now respects $verifiedData['needs_review'] so Partial results are correctly flagged in bulk runs.

[1.0.41-beta] - 2026-05-17

Fixed

  • needs_review not set on partial/flagged verification results. applyVerification() stored the provider's needs_review signal only inside the verification_metadata JSON blob, never in the indexed needs_review column. As a result, Smarty Partial results (and any provider returning needs_review: true) left the DB column false, so the amber "Needs Review" badge never appeared after verification. Three-part fix: (1) applyVerification() now sets $components['needs_review'] from the provider's metadata on the success path; (2) the form's no-changes verification path now reads $components['needs_review'] instead of hardcoding false; (3) _verified_verification_state now carries needs_review and the Accept handler uses it instead of hardcoding false.

[1.0.40-beta] - 2026-05-15

Fixed

  • Dual-spinner bug on "Verify Address" click. When the "Accept Address" and "Verify Address" buttons shared a single Actions::make() container with an explicit key, both spinners fired simultaneously on any click. Root cause: the shared key caused Filament to inject a schemaComponent parameter into both wire:click handlers via Js::from(), producing JSON.parse(...) in the wire:target attribute. Alpine's evaluate() could not parse that expression, so Livewire fell back to method-name-only matching and activated all mountAction spinners. Fix: the "Accept Address" button is now in its own unkeyed container, removing the JSON.parse from its target and scoping each spinner correctly. The "Verify Address" container retains its key for test compatibility.
  • "Changes detected" footer showed 0 fields after manual verification. When the Verify Address button detected differences and showed the standardization dialog, the _changes field was stored as a JSON array (["address_line_1"]) rather than a JSON object. The comparison blade template calls array_keys() on the decoded value, expecting field names as keys — but got numeric indices ([0, 1]) instead, producing empty labels. Fixed to use array_fill_keys($changes, true) (matching the blur-geocoding path). Added regression test.

[1.0.39-beta] - 2026-05-15

Changed

  • README: accurate per-suite test counts (Unit 249, Integration 787, Feature 72, Smoke 73, Performance 37); smart auto-routing description updated.
  • Docs: Last updated dates corrected to 2026-05-13 for all files modified in that session (VERIFICATION.md, CONFIGURATION-GUIDE.md, FAQ.md, USER-GUIDE.md, PANEL-SETUP.md, UI-STYLE-GUIDE.md, CUSTOMIZATION_GUIDE.md, BETA-TESTING.md).

[1.0.38-beta] - 2026-05-13

Fixed

  • USPS secondary address acronyms rendered in title case. titleCase() was correctly normalising USPS ALL-CAPS responses but had no exception for acronyms in secondary address lines. PMB 42 became Pmb 42, PO Box became Po Box, etc. A second acronym-restore pass (after the existing directionals pass) now keeps PMB, PO, APO, FPO, DPO, RR, and HC in uppercase. 2 new tests.

[1.0.37-beta] - 2026-05-13

Added

  • Filament v5 / Livewire v4 compatibility. Composer constraint updated to filament/filament: ^4.0|^5.0. Confirmed compatible via source-level audit: dispatch() browser events, wire:model.live + programmatic input events, window.Livewire.find().call(), and action modal state path (mountedActions.{n}.data) are all unchanged in Livewire v4 / Filament v5. illuminate/support widened to ^11.0|^12.0|^13.0. PHP simplified to ^8.2.
  • Smart auto-routing in auto mode. In auto mode, provider selection is now country-aware rather than a flat priority list. Countries where Smarty has DeliveryPoint-level precision (155 countries including CA, AU, GB, FR, DE, BR, IL, SG, and others) route to Smarty first; all others (JP, CN, etc.) route to Google first. USPS country pins and explicit provider overrides are unaffected. Routing decisions are recorded in Log::debug with country_code, smarty_strong, and provider_selected keys. Notable exclusions from the Smarty-first list: JP (empirically unreliable), CN (Thoroughfare precision only — causes false Partial matches), KR (not in Google's supported country list). 10 new routing tests.

Changed

  • All user-facing docs updated to reflect Filament 4 and 5 compatibility, Laravel 11–13 support, and current test count (1,220+).
  • VERIFICATION.md: Auto provider selection section updated for smart routing; Google vs Smarty comparison table expanded with diacritics/accent behaviour row; Smarty International uppercase ASCII (postal-authority standard) documented with examples and "Keep My Version" guidance.
  • CONFIGURATION-GUIDE.md: auto provider description, troubleshooting fix #3, and International Application recipe updated for smart routing.

[1.0.36-beta] - 2026-05-13

Fixed

  • Brazilian addresses: bairro (neighbourhood) stored as city. For Brazilian cities, Google's Geocoding API returns the municipality at administrative_area_level_2 (not locality), while sublocality_level_1 contains the bairro/neighbourhood. DefaultAddressExtractor's city priority chain fell through to sublocality_level_1 before administrative_area_level_2, so the bairro ("Jardim Paulista", "Centro", "Copacabana") was stored as the city. This caused Smarty verification to receive the bairro as locality, leading to wrong-city matches (e.g. "Rua XV de Novembro 1500, Centro" matched to Palotina instead of Curitiba). Fix: for BR (and other countries in a new ADMIN_LEVEL_2_CITY_COUNTRIES constant), the extractor now resolves city post-loop with country-aware priority — administrative_area_level_2 takes city, sublocality_level_1 goes to dependent_locality. 4 new unit tests.

[1.0.35-beta] - 2026-05-12

Fixed

  • Accented characters dropped during address field uppercasing. strtoupper() is ASCII-only — fields subject to uppercase_fields formatting (e.g. Brazilian locality "Comércio") were rendered as "COMéRCIO" instead of "COMÉRCIO". Same issue affected country names appended to international-format previews (e.g. "México" → "MéXICO"). Both calls replaced with mb_strtoupper($value, 'UTF-8'). 1 new integration test.

[1.0.34-beta] - 2026-05-10

Fixed

  • License activation warning repeated on every container wake-up. When FINGERPRINT_ALREADY_EXISTS was returned by Anystack (activation record lost, fingerprint still registered), LicenseService::activate() logged the warning and returned without writing anything to the address_license_activation table. On ephemeral hosts (Laravel Cloud, Fly.io), this caused the warning to fire on every container restart or hibernation wake-up. A placeholder record (with null activation_id/license_id) is now stored so hasActivation() returns true on subsequent checks, preventing the repeat. validate() skips gracefully when activation_id is null. The warning still fires once to prompt the operator to deauthorize and redeploy for full tracking.

[1.0.33-beta] - 2026-05-09

Fixed

  • License activation migration not registered in service provider. The address_license_activation table migration added in v1.0.32-beta was missing from the hasMigrations() list in FilamentAddressServiceProvider, so php artisan migrate did not discover or run it on installed instances.

[1.0.32-beta] - 2026-05-09

Fixed

  • License activation record moved to a dedicated database table. LicenseService previously stored the activation record in Cache::store('file'). On ephemeral-filesystem hosts (Laravel Cloud, Fly.io, any container-based deployment), the file cache is wiped on every container restart — causing hasActivation() to return false, activate() to fire, Anystack to return FINGERPRINT_ALREADY_EXISTS, and a warning to be logged on every restart cycle. The activation record is now stored in a dedicated address_license_activation database table (added via migration). A migration-created table is unaffected by php artisan cache:clear, survives container restarts on any hosting environment, and requires no additional configuration. The file-cache backoff in the service provider is unchanged — it is transient state that does not need to survive restarts.

[1.0.31-beta] - 2026-05-05

Fixed

  • isNamedPoi false positive for Israeli/RTL-style address format. Google Places returns the place name for Israeli addresses with the street number at the end ("Ranak St 19", "Derekh ha-Dukhifat 410"), not the beginning. The named-POI guard only checked for a leading digit (/^\d/), so these passed the check and the place name was incorrectly written into address_line_2. Guard updated to /^\d|\s\d+$/ — also blocks names that end with a space-separated number.

[1.0.30-beta] - 2026-05-04

Fixed

  • License activation record stored in file cache (not default cache store). The activation record was previously stored in the default cache store (typically database-backed). A DB crash or cache flush would lose the record, causing hasActivation() to return false, activate() to fire, Anystack to return FINGERPRINT_ALREADY_EXISTS, and a warning to be logged — repeating every 5 minutes until the operator intervened. The activation record is now stored via Cache::store('file'), matching the backoff key store added in v1.0.28-beta. Survives DB cache clears and DB crashes.
  • FINGERPRINT_ALREADY_EXISTS backoff increased from 5 minutes to 4 hours. This condition requires manual intervention (php artisan addresses:license:deactivate + redeploy) and will not self-heal on retry. A 5-minute backoff spammed logs every 5 minutes; 4 hours gives breathing room while still surfacing the problem regularly.
  • Livewire trigger simplification in autocomplete mode. Removed redundant change event dispatch and component.set() call from the Places selection handler (address-search-field.js). A single input event is sufficient for wire:model.live; the extra dispatches were triggering duplicate Livewire requests.

[1.0.29-beta] - 2026-05-03

Fixed

  • Country-aware address parsing. AddressInputParser methods now accept a $countryCode parameter, gating country-specific logic correctly:
    • Named-building heuristic ("The Shard, 32 London Bridge St" → line 1 is the street) is now restricted to Western-address countries (GB, US, AU, CA, NZ, IE). Previously it misfired on FR/JP/CN/KR addresses where neighbourhood or ward names precede the street.
    • Japanese building extractor is now gated to JP only. Previously it included English keywords (Building, Tower, Apartment), causing false matches on English input (e.g. "Empire State Building, 350 Fifth Ave"). The fragile ordering workaround is removed.
    • All call sites in AddressForm and GeocodingQueryBuilder now pass the active country code.
    • 5 new unit tests added.

[1.0.28-beta] - 2026-05-03

Fixed

  • License activation warning spam on database failure. Two bugs caused the "activation record was lost" warning to fire on every request when the database cache was unavailable (e.g. periodic DB crashes on Laravel Cloud):
    1. The FINGERPRINT_ALREADY_EXISTS path in activate() logs a warning and returns without throwing an exception — the service provider's catch blocks never run, so no backoff was ever set for this code path.
    2. All backoff writes used the default (DB-backed) Cache facade. When the database cache is down, Cache::put() silently fails, so even the catch-block backoffs didn't persist between requests. Fixed by: (a) switching all backoff reads/writes to Cache::store('file') — available even when the DB cache is down; (b) adding a post-try check that sets the file-backed backoff whenever hasActivation() is still false after the activation attempt, catching the silent FINGERPRINT return path.

[1.0.27-beta] - 2026-05-03

Added

  • US Military (APO/FPO/DPO) address support. Military addresses use pseudo-city codes (APO, FPO, DPO) and special state codes (AE/AP/AA). They have no street number and cannot be geocoded by Google. Three entry points were blocked:
    • Pre-flight validator (GeocodingQueryValidator): added MILITARY_ADDRESS_PATTERN bypass alongside the existing PO Box and Rural Route bypasses. APO/FPO/DPO queries now pass pre-flight without triggering "Street number appears to be missing."
    • Blur geocoding routing (AddressForm): military addresses skip the geocoding call entirely and route directly to verifyFromComponents, avoiding a doomed API call to Google.
    • Aggressive smart-detection mode (AddressForm): the street-number requirement for smart_detection_level=aggressive is now bypassed for military addresses.
    • USPS verifies APO/FPO/DPO addresses natively via DPV; post-verification geocoding failure (no coordinates) is non-fatal and expected.
    • 3 corpus fixtures (APO AE, APO AP, FPO AP) and 9 new unit tests added.

[1.0.26-beta] - 2026-05-03

Fixed

  • CN: Smarty International can return a false "Partial" match with a completely wrong city/state for Chinese addresses (e.g. a Beijing Chaoyang address matched to Tonghua, Jilin Province). applyVerification was applying Smarty's administrative fields unconditionally, overwriting the correct geocoded values from Google. Added a guard: for CN addresses, city/state/district from Smarty are only applied when the verification status is "Verified" (which essentially never occurs for CN); otherwise the geocoded values from ChineseAddressExtractor + Google are preserved. Smarty's address_line_1 normalisation (for display in the choice dialog) is unaffected.

[1.0.25-beta] - 2026-05-02

Fixed

  • GB: postal_town not included in city extraction priority chain; addresses in Greater London and other postal_town-only areas returned the admin area name instead of the city. Fixed in DefaultAddressExtractor.
  • GB: Google Places returns outward-code-only UK postcodes (e.g. "W1U") when the full postcode is known. Fixed in AddressProcessingService — the longer of the two postcodes wins when one is a prefix of the other.
  • GB: Smarty International reverses UK flat address lines, placing the subpremise in address_line_1. Added $isGarbledSubpremise structural guard in AddressForm to detect and correct the swap.
  • GB: Named building prefix (e.g. "The Shard, 32 London Bridge Street") was lost by the parser — treated as a street name rather than a building + street combination. Fixed in AddressInputParser.extractStreetFromFullInput.
  • GB: Scottish/English L1 state (Scotland, England) was lost when Smarty returned a granular admin area (Midlothian, Lanarkshire). AddressProcessingService now preserves geocoded_state for L1 fallback matching.
  • GB: Missing postal code caused Smarty to return Ambiguous for otherwise valid addresses. AddressProcessingService now recovers the postcode from the user's input string (country-aware patterns) before sending to verification.
  • GB: AddressDuplicateDetector did not normalise em/en dashes or spaces around hyphens in range street numbers (e.g. "21–23" vs "21-23" vs "21 - 23"); duplicate addresses with typographic variants were stored as distinct records. Fixed in normalizeAddress().
  • GB: $isGarbledSubpremise guard in AddressForm did not match slash notation ("6/1"); extractSecondaryAddress flat patterns did not capture the full sub-unit with slash. Both fixed.
  • GB: extractSecondaryAddress matched "unit" as a word boundary inside "United Kingdom", producing "Unit ed" in address_line_2. Fixed with \b word boundary guard.
  • US: $isNamedPoi false positive for Queens-style hyphenated street numbers (e.g. "30-30 Northern Blvd"). Simplified guard to /^\d/ (any leading digit = street number).
  • CA: Duplicate unit in address_line_2 when user input uses Canadian unit-hyphen-civic format (e.g. "305-1245 Robson St"). The unit is already encoded in address_line_1; added $isCanadianHyphenNotationLine1 guard in AddressForm.
  • CA: "Changes detected: 0" shown in the blur-path choice dialog. The _changes array was stored as an indexed JSON array; the Blade template's array_keys() call returned [0]. Fixed with array_fill_keys($changes, true).
  • CA: PO Box postal code was silently overwritten by the geocoded civic street postal. extractPostalCodeFromInput was called without a country code, defaulting to US ZIP patterns. Fixed by computing $extractedPostal with the form's country code and using it throughout.
  • CA/AU/UK: Same missing country code in extractPostalCodeFromInput meant non-US postal codes were not extracted for the originalData comparison in the choice dialog.
  • CA: Rural Route addresses ("RR 1", "SS 2", "MR 3") were blocked by the pre-flight geocoding validator. Added RURAL_ROUTE_PATTERN bypass alongside the existing PO Box bypass in GeocodingQueryValidator.

[1.0.24-beta] - 2026-04-28

Fixed

  • Production DB hammering: all Anystack API calls now send Accept: application/json. Previously the API returned HTML 404 pages, causing a JSON parse exception on every request.
  • FINGERPRINT_ALREADY_EXISTS check now reads the message key from Anystack's response body (was reading error, which is not present in the actual response format).
  • 5-minute backoff cache added to FilamentAddressServiceProvider to prevent repeated license activation retries on every request when the Anystack API is unreachable or returns an error.

[1.0.23-beta] - 2026-04-28

Fixed

  • Smarty International API returns the district for China's direct-controlled municipalities (Beijing, Shanghai, Tianjin, Chongqing) in dependent_locality (level 3), echoing the municipality name at both administrative_area and locality. This conflicts with the package's database structure where districts are at level 2 — the direct child of the municipality with no prefecture-level city in between. The standardized choice dialog showed the district at level 3, appearing to differ from the user's level 2 input even though both referred to the same place. Fixed by detecting this pattern in parseIntlResponse() and promoting dependent_localitylocality for CN municipalities.

[1.0.22-beta] - 2026-04-27

Fixed

  • SmartyUsageWidget showed the US section even when ADDRESS_VERIFICATION_SMARTY_US_ENABLED=false. The show flag was driven purely by usage data and limits; the template's fallback logic forced at least one section to render, overriding the disabled state. Fixed by forcing show = false in getStats() for any disabled section, so the template fallback cannot override it.

[1.0.21-beta] - 2026-04-27

Fixed

  • SmartyUsageWidget and UspsRateLimitWidget appeared in the dashboard whenever provider credentials were configured, ignoring the enabled flags (ADDRESS_VERIFICATION_SMARTY_US_ENABLED, ADDRESS_VERIFICATION_SMARTY_INTERNATIONAL_ENABLED, ADDRESS_VERIFICATION_USPS_ENABLED). Widgets now require both credentials and the provider being enabled — consistent with isAvailableForCountry() behaviour. This allows credentials to remain in place while a provider is temporarily disabled.

[1.0.20-beta] - 2026-04-26

Fixed

  • Migration create_addresses_table failed on SQLite and PostgreSQL due to MySQL-specific column prefix index syntax (address_line_1(50)). SQLite is Laravel's default database since Laravel 11, so this broke fresh installs following the quickstart. Fixed by wrapping the index creation in a driver check; MySQL/MariaDB use the prefix index, all other drivers use a plain index.

[1.0.19-beta] - 2026-04-24

Fixed

  • "Verify Address" / "Accept Address" buttons invisible on Filament v4.10+ in non-panel Livewire contexts (e.g. the public demo). Filament\Actions\Action closures were typed callable $get, relying on name-based dependency injection (getSchemaComponent()->makeGetUtility()). In Filament v4.10+, getSchemaComponent() returns null outside a panel page, causing the injection to silently fail and all action visibility closures to evaluate as hidden. Fixed by typing $get / $set parameters as \Filament\Schemas\Components\Utilities\Get / Set, which uses type-based resolution and works in all contexts. Backwards-compatible: on Filament v4.2, name-based resolution fires first (matching the parameter name 'get' before the type), so behaviour is unchanged.

[1.0.18-beta] - 2026-04-24

Fixed

  • 500 error on first request after deploy when Anystack license API times out during activation. The ServiceProvider activate() call was only catching LicenseActivationException; a cURL timeout throws ConnectionException which propagated uncaught. Added catch (\Throwable) to log a warning and continue — consistent with the existing validate() error handling. The next request retries activation when the API is reachable.

[1.0.17-beta] - 2026-04-24

Fixed

  • Quality score badge was invisible in Geocoding mode when no verification provider is configured (ADDRESS_VERIFICATION_PROVIDER=none). The visible() condition now also triggers when geocoding coordinates are present (lat is set), so the score appears after a successful blur geocode regardless of verification state.
  • Pasted address with a labeled premise (e.g. "Suite 2115", "Apt 1B") was written to address_line_2 as a bare unit code (#2115, 1B) after blur geocoding. Google strips the label from the subpremise component; the extractor has no access to the original input. Fix: the blur geocoding handler now detects when the geocoded address_line_2 is a bare unit code (with or without # prefix) and the parser extracted a labeled version from the user's input — the labeled version is preferred.

Changed

  • docs/USER-GUIDE.md: clarified the subdivision section — subdivisions are linked by stable, canonical ULID identifiers, not text strings. Added concrete example: renaming a subdivision in seed data updates every address referencing that ID automatically.

[1.0.16-beta] - 2026-04-20

Fixed

  • Verification state cleared after accepting the USPS standardized version in the choice dialog. onAddressFieldEdit was missing the _input_source guard documented in its own docblock. Filament defers afterStateUpdated callbacks until after the originating closure completes — by the time address field callbacks fired, is_verified was already true, causing verification to be erroneously cleared and needs_review saved to the database. Fix: added guard for 'blur', 'reverse', and 'choice' sources; both accept paths now set _input_source='choice' before applying fields.
  • onAddressFieldEdit guard was too broad on edit forms. The 'hydrating' source set during form load was never cleared (it clears in lat.afterStateUpdated, which only fires when lat changes — not on edit page load). Including it in the guard permanently blocked verification clearing for all user edits on the edit page.
  • Geocoding "Verifying address…" spinner toast stuck on screen when pre-flight validation failed (e.g. missing street number). The JS blur handler showed the toast before the Livewire request; when pre-flight returned early, address:geocoding-complete was never dispatched. Fix: dispatch the event and show an "Address incomplete" warning notification.

Changed

  • address_line_1 is now trimmed of leading/trailing whitespace on blur (common from copy-paste).

[1.0.15-beta] - 2026-04-20

Fixed

  • AddressVerificationService::isAvailableForCountry() now respects the configured verification provider (ADDRESS_VERIFICATION_PROVIDER). Previously it used an auto-scan of all registered providers, which could make the Verify button appear for countries the configured provider does not support (e.g. showing Verify for France when provider=usps). The button would appear but verification would silently do nothing. Now the check passes the configured provider to selectProviders() so button visibility matches actual verification capability.

[1.0.14-beta] - 2026-04-19

Fixed

  • processFromPlace: reverse geocoding a Places autocomplete selection could inject a subpremise (e.g. #305) into address_line_2 when Google returns a unit-level result for the building's coordinates. The place selection is authoritative — address_line_2 and subpremise from the reverse geocode are now cleared before the result is returned.
  • License check skipped for APP_ENV=local and APP_ENV=testing environments. Previously, copying a production .env to a local app would trigger an activation attempt and consume an activation slot (or fail with "Invalid license key" if the limit was reached).

Docs

  • docs/LICENSING.md: Updated "Local Development" and "CI/CD" sections to reflect APP_ENV-based skip. Updated "How Activation Works" to describe Cache storage instead of a filesystem file. Updated FAQ entries accordingly. Replaced "corrupt activation file" language with "corrupt activation record".
  • docs/CONFIGURATION-GUIDE.md, INSTALLATION.md: Removed stale references to storage/app/.license-activation.json (now stored in Laravel Cache).
  • docs/PANEL-SETUP.md: Config reference updated to show ADDRESS_PANEL_ID/PATH=addressing (was app).

[1.0.13-beta] - 2026-04-19

Fixed

  • License activation record now stored in Laravel Cache instead of the local filesystem. Fixes silent activation failures on ephemeral-storage hosts (Laravel Cloud, Vapor, Fly.io) where storage/app/ is wiped on every deploy, forcing a re-activation call that Anystack rejects with FINGERPRINT_ALREADY_EXISTS.
  • FINGERPRINT_ALREADY_EXISTS (422) response from Anystack is now handled gracefully: logs a warning and continues rather than throwing LicenseActivationException. This confirms the key is valid and the installation was previously activated — the local record was simply lost.

Docs

  • docs/PANEL-SETUP.md: Added "Prerequisite: FilamentUser" section. Filament v4 requires the User model to implement FilamentUser with canAccessPanel() before any panel grants access. Apps using filament:install --panels get this automatically; apps using AddressingPanelProvider directly (Option 1) must add it manually.
  • AddressingPanelProvider: Docblock now includes the required FilamentUser snippet.
  • README.md, docs/USER-GUIDE.md: Added links to INSTALLATION.md for first-time setup (beta access, Composer auth, CI/CD, scenario walkthroughs).

[1.0.12-beta] - 2026-04-15

Fixed

  • needs_review flag incorrectly set after blur geocoding when ADDRESS_VERIFICATION_PROVIDER=none. The verification service returned a failure result with an error string ('No provider available for this country') even when verification was intentionally disabled. That error propagated into components['verification_error'], triggering the "Address not verified" warning and needs_review=true. Fixed by returning a silent failure (no error string) when no providers are available — callers now treat it as "skipped" rather than "failed".
  • Verify Address button now hidden when ADDRESS_VERIFICATION_PROVIDER=none. Previously, isAvailableForCountry() used auto-detection (ignoring the configured provider) and could show the button even though clicking it would do nothing useful.

[1.0.11-beta] - 2026-04-15

Fixed

  • Double Livewire request on autocomplete selection in page-form mode. address-search-field.js was firing both a change event and an explicit component.set() call after the input event — all three triggered separate Livewire round-trips. Removed the redundant change dispatch and component.set() call; a single input event is sufficient for wire:model.live in Livewire 3.

Changed

  • Production license enforcement: a missing FILAMENT_ADDRESS_LICENSE_KEY now throws LicenseActivationException when APP_ENV=production. Previously the package silently skipped license validation with no key configured, making it impossible to detect bootleg distribution. Local and staging environments continue to boot without a key.

Docs

  • Two-key Google Maps setup (GOOGLE_MAPS_SERVER_KEY + GOOGLE_MAPS_BROWSER_KEY) promoted as the primary recommendation in INSTALLATION.md and docs/GETTING-STARTED.md. Single-key GOOGLE_MAPS_API_KEY fallback moved to a commented secondary option.

[1.0.10-beta] - 2026-04-13

Fixed

  • GOOGLE_MAPS_API_KEY single-key fallback was ignored by both MapImageController (static map proxy) and AddressSearchField (Places autocomplete). Only the specific GOOGLE_MAPS_SERVER_KEY / GOOGLE_MAPS_BROWSER_KEY configs were checked. Customers using the documented single-key setup got 500 errors on the map proxy and no autocomplete. Both now fall back to GOOGLE_MAPS_API_KEY when their specific key is unset.

Docs

  • Added docs/GETTING-STARTED.md — quick-start guide covering installation, configuration, and first address form.
  • Added docs/BETA-TESTING.md — beta program overview and feedback instructions.

[1.0.9-beta] - 2026-04-13

Fixed

  • DeleteBulkAction ("Delete Selection") was visible to all authenticated users because AddressPolicy lacked a deleteAny() method. Filament checks deleteAny to gate the bulk delete button — without it the check fell through to true. Added deleteAny() mirroring the same Spatie/config logic as delete().

Docs

  • SPATIE-PERMISSION.md, AUTHORIZATION.md: delete-addresses permission now documented as also controlling the "Delete Selection" bulk action.

[1.0.6-beta] - 2026-04-13

Fixed

  • RecalculateQualityAction (bulk action) and RecalculateAllQualityAction (header action) were visible to all authenticated users regardless of permissions. Both now check the update policy before rendering, so viewer-only roles no longer see either.

Docs

  • INSTALLATION.md: Added Step 8b documenting that config('addresses.import.allowed_entity_types') must be populated for the standalone Addresses resource create form to work. Without this, the "Belongs To" dropdown is empty and addresses cannot be created.
  • docs/SPATIE-PERMISSION.md: Added "Register Entity Types" section covering the same requirement in the Spatie integration context.

[1.0.5-beta] - 2026-04-13

Fixed

  • LicenseActivationException no longer escapes the boot hook and crashes the entire app when an invalid or expired FILAMENT_ADDRESS_LICENSE_KEY is configured. The exception is now caught in packageBooted(), logged as an error, and the app continues to boot. Customers with a bad key will see a log entry rather than a 500 error page.

[1.0.4-beta] - 2026-04-13

Fixed

  • php artisan migrate now auto-discovers package migrations without --path=. Added runsMigrations() to FilamentAddressServiceProvider::configurePackage().
  • ADDRESS_VERIFICATION_PROVIDER=none no longer logs a spurious "Unknown verification provider" warning on every verification attempt. The value is now treated as an explicit opt-out and short-circuits silently in selectProviders().

Docs

  • INSTALLATION.md: Google Maps API key is now correctly labelled optional (not "Required"). Added graceful-degradation note explaining what works without a key.
  • INSTALLATION.md: Added php artisan addresses:install wizard as the recommended setup path (Option A in Step 7). Added migration troubleshooting note for --path= workaround.
  • config/addresses.php: none provider comment clarified — it suppresses verification silently with no log noise. Documents ADDRESS_VERIFICATION_ENABLED=false as the way to disable both verification and geocoding.

[1.0.1-beta] - 2026-04-11

Added

  • Runtime license validation via Anystack API (LicenseService) — activates on boot, caches result for configurable TTL, handles expiry gracefully
  • php artisan addresses:license:deactivate command to release an activation slot before moving to a new domain
  • LicenseActivationException for structured error handling during activation failures

Changed

  • FilamentAddressServiceProvider::boot() now validates the license key on each request (within TTL cache window)
  • config/addresses.php gains license.* keys (key, product_id, api_token, cache_ttl)
  • Pest constraint bumped to ^4.0; phpunit to ^11.0|^12.0 in require-dev

Fixed

  • Pest 4 base-class-per-folder compatibility: LicenseServiceTest moved to Integration/Services/; added explicit uses(PureUnitTestCase::class) to five Unit test files that had no base-class declaration

[1.0.0-beta] - 2026-04-09

Initial beta release.

Released under a commercial license.