The public storefront is mounted under /t (ThemeV2PublicEndpoints.cs doesMapGroup("/t")), with the organisation resolved from the request host. GET /t/{**path} landsin GetThemeV2Page.Endpoint, which whitelists the query string (location, category,attributes, embed) and calls IThemeV2RenderService.RenderPathAsync. Because the storefrontlives under /t, every internal link a template emits must be /t/... — that is what thestorefront_url filter is for (see §4.4).
The flow inside RenderPathAsync:
- Route resolve —
ThemeV2RouteResolver.Resolve(path)maps the URL to aThemeV2RouteResult: a template path, a data scope, an optional slug/location slug, and anIsEmbedflag. Paths starting withapi,assets,account,checkout,favicon.ico,robots.txtorsitemap.xmlare excluded up front (they belong to the API / Angular SPA). - Org theme lookup — the active
OrganisationThemeV2row is loaded (and lazily auto-provisioned against the base theme if the org predates ThemeV2). This row carries the all-importantCacheVersiontoken. - Rendered-page cache check — if a non-draft render of this exact org + path + version + filter combination is cached, it is returned immediately.
- File resolution —
LoadResolvedFilesAsyncbuilds one dictionary of every theme file (base overlaid by org overrides — see §6), stripping{% schema %}blocks from.fluidfiles and pulling section schemas out for default hydration. Binary assets are skipped; only.css/.jsassets flow through because the layout inlines them. - Context build —
BuildRenderContextAsyncruns the queries for the route's data scope and produces theThemeV2RenderContext(§4). Section settings are hydrated against the schemas (ThemeV2SectionHydrator) so missing values pick up schema defaults and empty block lists are seeded fromdefaultBlocks— the storefront and the builder inspector always agree. - Page render — the route's template is parsed (cached) and rendered with the context. If the template file doesn't exist and the route is a
Pagescope matching a stored CMS page,templates/page.fluidis used; otherwisetemplates/404.fluid. - Layout render — the page HTML becomes
content_for_layouton aThemeV2LayoutContext, along with the inlinedassets/theme.cssandassets/theme.js, and the layout renders around it. Embed requests preferlayouts/embed.fluid, falling back tolayouts/main.fluid(which is mandatory — the renderer throws without it). - Cache write — the full HTML is cached (non-draft renders only).
Rendering is defensive against tenant-authored templates: MaxRecursion = 100,MaxSteps = 100_000, and a 10-second wall-clock render timeout.