Status: Done Completed: 2026-02-19 Priority: P2 Created: 2026-02-19 Owner: —
The routing-rewrite branch added URL codec protocols, browser history providers, URL sync, cross-chart routing, deep busy checking, and validation. This code currently lives in ui_routes.cljc (grown from 650→1194 lines) plus 3 already-extracted sibling files (route_url.cljc 224 lines, url_codec.cljc 217 lines, url_codec_transit.cljc 120 lines).
ui_routes on main is published API. We need backward compatibility for existing users while making the new routing system available under a clean package.
com.fulcrologic.statecharts.integration.fulcro.routing.*ui_routes.cljc to main's 650-line version + deprecation docstring pointing to routing.*ui_routes_options.cljc to main's version (remove checks def — it's a fn arg to start!/install-url-sync!, not a component option)ui_routes keeps main's behavior, new features are routing.*-onlyui_routes_test.cljc to main's 109-line version; move the 743 lines of new test code to routing/c.f.s.i.f.routing/
core.cljc ;; Full routing API: DSL, URL sync, validation, busy checking, rendering
url_codec.cljc ;; URLCodec protocol (moved from existing url_codec.cljc)
url_codec_transit.cljc ;; TransitBase64Codec (moved from existing url_codec_transit.cljc)
url_history.cljc ;; URLHistoryProvider protocol + shared helpers (no implementations)
browser_history.cljc ;; BrowserURLHistory deftype + factory (CLJS-only content)
simulated_history.cljc ;; SimulatedURLHistory defrecord + factory + inspection helpers
src/main/com/fulcrologic/statecharts/integration/fulcro/routing/
src/test/com/fulcrologic/statecharts/integration/fulcro/routing/
These files already exist as standalone, zero-coupling-to-ui_routes files:
url_codec.cljc → routing/url_codec.cljc
c.f.s.i.f.url-codec → c.f.s.i.f.routing.url-codecstatecharts, protocols, clojure.set, clojure.stringURLCodec protocol, element-segment, path-from-configuration, params-from-configuration, configuration->url, deep-configuration->urlurl_codec_transit.cljc → routing/url_codec_transit.cljc
c.f.s.i.f.url-codec-transit → c.f.s.i.f.routing.url-codec-transitc.f.s.i.f.url-codec → c.f.s.i.f.routing.url-codecTransitBase64Codec, transit-base64-codec factory, base64 helpers, param encoding/decodingroute_url.cljc → split into 3 files for CLJS dead-code elimination
CLJS cannot DCE protocols, so implementations must be in separate namespaces.
Apps that only use BrowserURLHistory won't bundle SimulatedURLHistory and vice versa.
routing/url_history.cljc — Protocol + shared helpers (DCE-friendly)
c.f.s.i.f.routing.url-historyc.f.s.i.f.routing.url-codec (for element-segment)route_url.cljc:
URLHistoryProvider protocol (lines 41-49)current-url, current-url-path — browser URL utilities (lines 19-31)push-url!, replace-url! — bare pushState/replaceState wrappers (lines 33-35)find-target-by-leaf-name, find-target-by-leaf-name-deep — URL restoration (lines 176-207)new-url-path — legacy compat (lines 213-224)routing/browser_history.cljc — Browser implementation (CLJS-only)
c.f.s.i.f.routing.browser-historyc.f.s.i.f.routing.url-history (for protocol)route_url.cljc:
BrowserURLHistory deftype (lines 56-82) — wraps js/window.history with monotonic indexbrowser-url-history factory fn (lines 84-91) — seeds initial entry with replaceStaterouting/simulated_history.cljc — Test implementation (cross-platform)
c.f.s.i.f.routing.simulated-historyc.f.s.i.f.routing.url-history (for protocol)route_url.cljc:
SimulatedURLHistory defrecord (lines 97-140) — atom-backed history stacksimulated-url-history factory fn (lines 142-151)history-stack, history-cursor, history-entries inspection helpers (lines 157-170) — these reach into SimulatedURLHistory's :state-atom internalsrouting/core.cljc — the entire branch ui_routes.cljc (lines 1-1194)New ns: c.f.s.i.f.routing.core
This is the complete new routing system — DSL, URL sync, and all. It's the branch's ui_routes.cljc with updated namespace and requires. One file, one require for users.
Requires (update from branch's ui_routes ns form):
c.f.s.i.f.route-url → c.f.s.i.f.routing.url-history (protocol + helpers only; core doesn't need browser/simulated impls directly)c.f.s.i.f.url-codec → c.f.s.i.f.routing.url-codecc.f.s.i.f.url-codec-transit → c.f.s.i.f.routing.url-codec-transitc.f.s.i.f.ui-routes-options :as uro (component option keywords — uro avoids RAD ro collision)Contents — the full file, organized by section:
Shared utilities (lines 36-57):
form?, rad-report?, ?!, coerce-to-keywordPublic constants (lines 32-34):
session-id (well-known ::session)Route name generation (lines 59-68):
route-to-event-nameValidation (lines 72-221 — BRANCH ONLY, not on main):
report-issue! (private), find-all-route-targets (private), find-all-reachable-targets (private)validate-duplicate-leaf-names, validate-reachable-collisions, validate-routing-rootcompute-segment-chain (private), validate-duplicate-segmentsvalidate-route-configurationRoute initialization (lines 223-327):
initialize-route!, replace-join!, find-parent-route (private), update-parent-query!establish-route-params-node — NOTE: branch version is simpler than main's (no URL history logic)DSL builders (lines 339-446):
rstate — NOTE: branch version throws if :id passed; uses :route/segment instead of :route/pathistate — branch version adds :route/reachable, ::pending-child-routeBusy checking (lines 448-507):
busy-form-handler, check-component-busy? (private)deep-busy? (private — BRANCH ONLY, walks invocation tree)busy?Route state (lines 509-648):
record-failed-route!, clear-override!, override-route!routing-info-state (def)find-reachable-owner (private — BRANCH ONLY)routes — NOTE: branch version has cross-chart routing via :route/reachableRegions + rendering (lines 649-706):
routing-regions, ui-current-subroute, ui-parallel-route, route-to!Inspection (lines 709-839):
has-routes?, leaf-route?session-statechart-id (private — BRANCH ONLY)deep-leaf-routes (private — BRANCH ONLY, walks invocation tree)active-leaf-routes — branch version uses deep-leaf-routesroute-denied?, force-continue-routing!, abandon-route-change!, send-to-self!current-invocation-configurationreachable-targets (BRANCH ONLY)Startup (lines 841-855):
start! — branch version validates configuration, replaces main's start-routing!Bidirectional URL synchronization (lines 857-1194):
runtime-atom, url-sync-state, swap-url-sync! (private helpers)url-sync-provider, url-sync-installed?find-root-session (private — walks parent chain)url-sync-on-save — the on-save hook that pushes URL stateresolve-route-and-navigate! (private — URL→route conversion)install-url-sync! — comprehensive installer with popstate handling, acceptance/denial state machine, settling behaviorroute-current-url, route-history-indexroute-sync-from-url!route-back!, route-forward!ui_routes.cljc to main's versiongit show main:src/main/.../ui_routes.cljc (650 lines)(ns com.fulcrologic.statecharts.integration.fulcro.ui-routes
"DEPRECATED. Use com.fulcrologic.statecharts.integration.fulcro.routing.core instead.
This namespace is maintained for backward compatibility. New projects should use
the routing.* package which adds URL synchronization, cross-chart routing via
:route/reachable, deep busy-checking, and route configuration validation.
ALPHA. This namespace's API is subject to change."
(:require ...)) ;; keep main's requires exactly
ui_routes_options.cljc to main's versiongit show main:src/main/.../ui_routes_options.cljc (35 lines)checks def (lines 36-42 on branch) — it's a fn keyword arg, not a component optionRevert ui_routes_test.cljc to main's version (109 lines)
git show main:src/test/.../ui_routes_test.cljchas-routes?, leaf-route?, route entry, busy checking, force route — all exist on mainMove new test code to routing/core_test.cljc
deep-leaf-routes (private), validate-*, establish-route-params-node, reachable-targets, deep-busy?, check-component-busy?, start!, install-url-sync!, url-sync-*, route-back/forward!, route-sync-from-url!, route-current-url, route-history-indexuir/ → sroute/, ru/ → ruh/ or rsh/, uc/ → ruc/, uct/ → ruct/, ro/ → uro/Move existing test files to routing/ (ns rename):
route_url_test.cljc → routing/url_codec_test.cljc
url-codec → routing.url-codecuc/element-segment — lives in url-codec, no history ns neededroute_url_history_spec.cljc → routing/simulated_history_spec.cljc
route-url → routing.simulated-history (for simulated-url-history, history-stack, etc.) + routing.url-history (for protocol fns if any), url-codec → routing.url-codec, url-codec-transit → routing.url-codec-transiturl_sync_headless_spec.cljc → routing/url_sync_headless_spec.cljc
route-url → routing.simulated-history (uses simulated-url-history), ui-routes → routing.core, url-codec → routing.url-codec, url-codec-transit → routing.url-codec-transiturl_codec.cljc (moved to routing/url_codec.cljc)url_codec_transit.cljc (moved to routing/url_codec_transit.cljc)route_url.cljc (moved to routing/url_history.cljc)c.f.s.i.f.url-codec, c.f.s.i.f.url-codec-transit, c.f.s.i.f.route-urlrouting/core.cljc does NOT still require the old pathsrouting-demo (v1) does NOT reference any of our namespaces — leave it alone.
routing-demo2 uses ui-routes throughout and must switch to routing.core.
src/routing-demo2/.../app.cljs (49 lines)
c.f.s.i.f.ui-routes → c.f.s.i.f.routing.core :as sroutec.f.s.i.f.routing.browser-history (for CLJS build to include the impl)uir/ → sroute/ in file bodysrc/routing-demo2/.../chart.cljc (123 lines)
c.f.s.i.f.ui-routes → c.f.s.i.f.routing.core :as srouteuir/ → sroute/ in file bodysrc/routing-demo2/.../admin_chart.cljc (56 lines)
c.f.s.i.f.ui-routes → c.f.s.i.f.routing.core :as srouteuir/ → sroute/ in file bodysrc/routing-demo2/.../ui.cljs (314 lines)
c.f.s.i.f.ui-routes → c.f.s.i.f.routing.core :as srouteui-routes-options :as ro → ui-routes-options :as uro (avoids RAD report-options collision)uir/ → sroute/ and ro/ → uro/ in the file bodysrc/test/.../routing_demo2/chart_test.cljc (230 lines)
c.f.s.i.f.ui-routes → c.f.s.i.f.routing.core :as srouteuir/ → sroute/ (used for route-to-event-name only)The routing section is 224 lines and references the old namespace paths throughout.
Namespace references to update:
com.fulcrologic.statecharts.integration.fulcro.ui-routes → c.f.s.i.f.routing.core (lines 1037, 1049-1052)com.fulcrologic.statecharts.integration.fulcro.route-url → split reference (line 1177, 1231, 1238)uir/ prefix references → sroute/, all ro/ → uro/Specific changes:
Line 1037-1039: Update opening paragraph ns reference to routing.core
Lines 1049-1052: Update example require:
;; OLD
[com.fulcrologic.statecharts.integration.fulcro.ui-routes :as uir]
;; NEW
[com.fulcrologic.statecharts.integration.fulcro.routing.core :as sroute]
All uir/ prefixes in Guide examples become sroute/, all ro/ become uro/.
Lines 1177-1184: "Key Functions" section — update ns reference from route-url to the split:
element-segment → routing.url-codecpath-from-configuration, configuration->url → routing.url-codecfind-target-by-leaf-name → routing.url-historyinstall-url-sync! uses these automaticallyLines 1229-1246: "URL Sync Provider Protocol" section — major rewrite needed:
route-url to the split namespacesurl-history, browser impl in browser-history, simulated impl in simulated-history;; OLD
(require '[com.fulcrologic.statecharts.integration.fulcro.route-url :as route-url])
(let [provider (route-url/simulated-url-history "/initial")]
(uir/install-url-sync! app {:provider provider}))
;; NEW
(require '[com.fulcrologic.statecharts.integration.fulcro.routing.simulated-history :as rsh])
(let [provider (rsh/simulated-url-history "/initial")]
(sroute/install-url-sync! app {:provider provider}))
route-url/history-stack → rsh/history-stack, etc.Add new subsection: "Headless Operation" (after "URL Sync Provider Protocol"):
Document that SimulatedURLHistory enables full routing testing without a browser:
:provider — install-url-sync! on CLJ throws without onehistory-stack, history-cursor, history-entries) enable assertions on navigation stateurl_sync_headless_spec.cljc test file serves as a comprehensive examplefulcro-appinstall-fulcro-statecharts!simulated-url-history with initial URLstart! then install-url-sync! with :providerurl-sync-on-save in the :on-save handlerroute-to!, assert with history-stack/history-cursorgo-back!/go-forward! on the providerAdd deprecation note at the top of the routing section:
NOTE: The legacy `com.fulcrologic.statecharts.integration.fulcro.ui-routes` namespace is
deprecated. All examples below use the new `routing.core` namespace. The old namespace
remains available for backward compatibility but does not include URL sync, cross-chart
routing, or configuration validation.
- `fulcro/routing/core.cljc` — Full routing API (DSL, URL sync, validation)
- `fulcro/routing/url_codec.cljc` — URLCodec protocol
- `fulcro/routing/url_codec_transit.cljc` — Default transit+base64 codec
- `fulcro/routing/url_history.cljc` — URLHistoryProvider protocol + helpers
- `fulcro/routing/browser_history.cljc` — Browser history (CLJS)
- `fulcro/routing/simulated_history.cljc` — Simulated history (testing)
fulcro/ui_routes.cljc entry with "(deprecated)" notefulcro/ui_routes_options.cljc entry (shared)fulcro/route_history.cljc entry (legacy RAD)routing/ packageroute-url or ui-routes in the routing context should mention the new packagec.f.s.i.f.url-codec, c.f.s.i.f.url-codec-transit, c.f.s.i.f.route-urlrouting/core.cljc does NOT still require the old pathsStandardized aliases for the new package. All routing namespaces use r-prefixed aliases
for visual grouping. The core ns uses sroute (statechart-route) to avoid src/source confusion.
| New namespace | Alias | Mnemonic | Old alias it replaces |
|---|---|---|---|
routing.core | sroute | statechart-route | uir (ui-routes) |
routing.url-codec | ruc | routing url codec | url-codec, uc |
routing.url-codec-transit | ruct | routing url codec transit | url-codec-transit, uct |
routing.url-history | ruh | routing url history | route-url, ru |
routing.browser-history | rbh | routing browser history | (new) |
routing.simulated-history | rsh | routing simulated history | (new) |
ui-routes-options | uro | ui-routes options | ro (conflicts with RAD report-options) |
Apply these aliases in:
routing/core.cljc internal requiresrouting/session-id, route-to-event-name, working memory, configuration). It's the "URL layer" of routing, not a standalone concern. One require gives users the full routing API.BrowserURLHistory and SimulatedURLHistory must live in their own namespaces. The protocol ns (url_history) has only the protocol + pure helper fns (which CAN be DCE'd). Production apps require browser-history; tests require simulated-history. Neither pulls in the other.ui_routes_options stays put and reverts to main: It's the shared component options contract. Keywords are qualified to the ui-routes ns but exported as vars, so both old and new code can use them without conflict. checks was incorrectly placed here — it's a fn arg, not a component option.ui_routes stays as main's version (working, tested). New features are routing-only. Clean separation.route_history.cljc untouched: The legacy RAD-era route history file stays where it is. Main's ui_routes still requires it.routing.core will use ::core/... keywords for its own state (e.g., ::core/failed-route-event). Component options still use ui-routes-options vars which resolve to ::ui-routes/... keywords — this is fine since they're read from component options maps, not from ns-qualified keywords directly.url_codec.cljc ← no routing deps (only statecharts core)
url_codec_transit.cljc ← url_codec
url_history.cljc ← url_codec (protocol + helpers only, no impls)
browser_history.cljc ← url_history (CLJS-only impl)
simulated_history.cljc ← url_history (cross-platform test impl)
core.cljc ← url_codec, url_codec_transit, url_history, ui_routes_options
Note: core.cljc depends on the protocol ns (url_history), not the implementations.
Users choose which impl to require based on their environment — browser-history in
production CLJS, simulated-history in tests. Neither is bundled unless required.
clojure -A:dev:test REPL starts without errorsui_routes_test (109 lines) passesshadow_cljs_checker.clj):
:ci-tests build:routing-demo2 build (exercises the updated demo)grep -r for dangling namespace references across src/, Guide.adoc, and all CLAUDE.md files:
c.f.s.i.f.url-codec (without .routing.) → 0 hitsc.f.s.i.f.url-codec-transit (without .routing.) → 0 hitsc.f.s.i.f.route-url → 0 hitsui-routes :as uir in demo/test files → 0 hits (all switched to routing.core :as sroute)make docs/index.html) — no broken referencesCan you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |