2023-07-12 11:28:07 +03:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta http-equiv = "x-ua-compatible" content = "ie=edge" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2023-07-25 10:58:48 +03:00
< meta name = "generator" content = "ExDoc v0.30.3" >
2023-07-12 11:28:07 +03:00
< meta name = "project" content = "Plausible v0.0.1" >
< title > Plausible.Site.Cache — Plausible v0.0.1< / title >
2023-07-25 10:58:48 +03:00
< link rel = "stylesheet" href = "dist/html-elixir-P5GXSCHE.css" / >
2023-07-12 11:28:07 +03:00
< script src = "dist/handlebars.runtime-NWIB6V2M.js" > < / script >
2023-07-25 10:58:48 +03:00
< script src = "dist/handlebars.templates-NBND3S2D.js" > < / script >
2023-09-21 13:56:55 +03:00
< script src = "dist/sidebar_items-186F154B.js" > < / script >
2023-07-12 11:28:07 +03:00
< script src = "docs_config.js" > < / script >
2023-07-25 10:58:48 +03:00
< script async src = "dist/html-CGDDOCMI.js" > < / script >
2023-07-12 11:28:07 +03:00
< / head >
< body data-type = "modules" class = "page-module" >
< script >
try {
var settings = JSON.parse(localStorage.getItem('ex_doc:settings') || '{}');
if (settings.theme === 'dark' ||
((settings.theme === 'system' || settings.theme == null) & &
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.body.classList.add('dark')
}
} catch (error) { }
< / script >
< div class = "main" >
< button class = "sidebar-button sidebar-toggle" aria-label = "toggle sidebar" >
< i class = "ri-menu-line ri-lg" title = "Collapse/expand sidebar" > < / i >
< / button >
< section class = "sidebar" >
< form class = "sidebar-search" action = "search.html" >
< button type = "submit" class = "search-button" aria-label = "Submit Search" >
< i class = "ri-search-2-line" aria-hidden = "true" title = "Submit search" > < / i >
< / button >
< button type = "button" tabindex = "-1" class = "search-close-button" aria-label = "Cancel Search" >
< i class = "ri-close-line ri-lg" aria-hidden = "true" title = "Cancel search" > < / i >
< / button >
< label class = "search-label" >
< p class = "sr-only" > Search< / p >
< input name = "q" type = "text" class = "search-input" placeholder = "Search..." aria-label = "Input your search terms" autocomplete = "off" autocorrect = "off" autocapitalize = "off" spellcheck = "false" / >
< / label >
< / form >
< div class = "autocomplete" >
< div class = "autocomplete-results" >
< / div >
< / div >
< div class = "sidebar-header" >
< a href = "readme.html" >
< img src = "assets/logo.png" alt = "Plausible" class = "sidebar-projectImage" >
< / a >
< div class = "sidebar-projectDetails" >
< a href = "readme.html" class = "sidebar-projectName" translate = "no" >
Plausible
< / a >
< div class = "sidebar-projectVersion" translate = "no" >
v0.0.1
< / div >
< / div >
< ul class = "sidebar-listNav" >
< li > < a id = "extras-list-link" href = "#full-list" > Pages< / a > < / li >
< li > < a id = "modules-list-link" href = "#full-list" > Modules< / a > < / li >
< li > < a id = "tasks-list-link" href = "#full-list" > < span translate = "no" > Mix< / span > Tasks< / a > < / li >
< / ul >
< / div >
< div class = "gradient" > < / div >
< ul id = "full-list" > < / ul >
< / section >
< section class = "content" >
< output role = "status" id = "toast" > < / output >
< div class = "content-outer" >
< div id = "content" class = "content-inner" >
< h1 >
< button class = "icon-action display-settings" >
< i class = "ri-settings-3-line" > < / i >
< span class = "sr-only" > Settings< / span >
< / button >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L1" title = "View Source" class = "icon-action" rel = "help" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< span translate = "no" > Plausible.Site.Cache< / span >
< small class = "app-vsn" translate = "no" > (Plausible v0.0.1)< / small >
< / h1 >
< section id = "moduledoc" >
< p > A " sites by domain" caching interface.< / p > < p > Serves as a thin wrapper around Cachex, but the underlying
implementation can be transparently swapped.< / p > < p > Even though the Cachex process is started, cache access is disabled
during tests via the < code class = "inline" > :sites_by_domain_cache_enabled< / code > application env key.
This can be overridden on case by case basis, using the child specs options.< / p > < p > NOTE: the cache allows lookups by both < code class = "inline" > domain< / code > and < code class = "inline" > domain_changed_from< / code >
fields - this is to allow traffic from sites whose domains changed within a certain
grace period (see: < code class = "inline" > Plausible.Site.Transfer< / code > ).< / p > < p > When Cache is disabled via application env, the < a href = "#get/1" > < code class = "inline" > get/1< / code > < / a > function
falls back to pure database lookups. This should help with introducing
cached lookups in existing code, so that no existing tests should break.< / p > < p > To differentiate cached Site structs from those retrieved directly from the
database, a virtual schema field < code class = "inline" > from_cache?: true< / code > is set.
This indicates the < a href = "Plausible.Site.html" > < code class = "inline" > Plausible.Site< / code > < / a > struct is incomplete in comparison to its
database counterpart -- to spare bandwidth and query execution time,
only selected database columns are retrieved and cached.< / p > < p > There are two modes of refreshing the cache: < code class = "inline" > :all< / code > and < code class = "inline" > :updated_recently< / code > .< / p > < ul > < li > < p > < code class = "inline" > :all< / code > means querying the database for all Site entries and should be done
periodically (via < code class = "inline" > Cache.Warmer< / code > ). All stale Cache entries are cleared
upon persisting the new batch.< / p > < / li > < li > < p > < code class = "inline" > :updated_recently< / code > attempts to re-query sites updated within the last
15 minutes only, to account for most recent changes. This refresh
is lighter on the database and is meant to be executed more frequently
(via < code class = "inline" > Cache.Warmer.RecentlyUpdated< / code > ).< / p > < / li > < / ul > < p > Refreshing the cache emits telemetry event defined as per < a href = "#telemetry_event_refresh/2" > < code class = "inline" > telemetry_event_refresh/2< / code > < / a > .< / p > < p > The < code class = "inline" > @cached_schema_fields< / code > attribute defines the list of DB columns
queried on each cache refresh.< / p > < p > Also see tests for more comprehensive examples.< / p >
< / section >
< section id = "summary" class = "details-list" >
< h1 class = "section-heading" >
< a class = "hover-link" href = "#summary" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
2023-07-25 10:58:48 +03:00
Summary
2023-07-12 11:28:07 +03:00
< / a >
< / h1 >
< div class = "summary-types summary" >
< h2 >
< a href = "#types" > Types< / a >
< / h2 >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#t:t/0" translate = "no" > t()< / a >
< / div >
< / div >
< / div >
< div class = "summary-functions summary" >
< h2 >
< a href = "#functions" > Functions< / a >
< / h2 >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#child_spec/1" translate = "no" > child_spec(opts)< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#enabled?/0" translate = "no" > enabled?()< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#get/2" translate = "no" > get(domain, opts \\ [])< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#get_site_id/2" translate = "no" > get_site_id(domain, opts \\ [])< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#hit_rate/1" translate = "no" > hit_rate(cache_name \\ :sites_by_domain)< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#merge/2" translate = "no" > merge(new_items, opts \\ [])< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#name/0" translate = "no" > name()< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#ready?/1" translate = "no" > ready?(cache_name \\ :sites_by_domain)< / a >
< / div >
< div class = "summary-synopsis" > < p > Ensures the cache has non-zero size unless no sites exist.
Useful for orchestrating app startup to prevent the service
going up asynchronously with an empty cache.< / p > < / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#refresh_all/1" translate = "no" > refresh_all(opts \\ [])< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#refresh_updated_recently/1" translate = "no" > refresh_updated_recently(opts \\ [])< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#size/1" translate = "no" > size(cache_name \\ :sites_by_domain)< / a >
< / div >
< / div >
< div class = "summary-row" >
< div class = "summary-signature" >
< a href = "#telemetry_event_refresh/2" translate = "no" > telemetry_event_refresh(cache_name \\ :sites_by_domain, mode)< / a >
< / div >
< / div >
< / div >
< / section >
< section id = "types" class = "details-list" >
< h1 class = "section-heading" >
< a class = "hover-link" href = "#types" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
Types
2023-07-25 10:58:48 +03:00
< / a >
2023-07-12 11:28:07 +03:00
< / h1 >
< div class = "types-list" >
< section class = "detail" id = "t:t/0" >
< div class = "detail-header" >
< a href = "#t:t/0" class = "detail-link" title = "Link to this type" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this type< / span >
< / a >
< h1 class = "signature" translate = "no" > t()< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L61" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @type< / span > t() :: < a href = "Plausible.Site.html#t:t/0" > Plausible.Site.t< / a > ()< / pre >
< / div >
< / section >
< / section >
< / div >
< / section >
< section id = "functions" class = "details-list" >
< h1 class = "section-heading" >
< a class = "hover-link" href = "#functions" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
Functions
2023-07-25 10:58:48 +03:00
< / a >
2023-07-12 11:28:07 +03:00
< / h1 >
< div class = "functions-list" >
< section class = "detail" id = "child_spec/1" >
< div class = "detail-header" >
< a href = "#child_spec/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > child_spec(opts)< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L67" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > child_spec(< a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: < a href = "https://hexdocs.pm/elixir/Supervisor.html#t:child_spec/0" > Supervisor.child_spec< / a > ()< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "enabled?/0" >
< div class = "detail-header" >
< a href = "#enabled?/0" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > enabled?()< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L212" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< / section >
< / section >
< section class = "detail" id = "get/2" >
< span id = "get/1" > < / span >
< div class = "detail-header" >
< a href = "#get/2" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > get(domain, opts \\ [])< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L165" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > get(< a href = "https://hexdocs.pm/elixir/String.html#t:t/0" > String.t< / a > (), < a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: < a href = "#t:t/0" > t< / a > () | nil< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "get_site_id/2" >
< span id = "get_site_id/1" > < / span >
< div class = "detail-header" >
< a href = "#get_site_id/2" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > get_site_id(domain, opts \\ [])< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L197" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > get_site_id(< a href = "https://hexdocs.pm/elixir/String.html#t:t/0" > String.t< / a > (), < a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: < a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > pos_integer< / a > () | nil< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "hit_rate/1" >
< span id = "hit_rate/0" > < / span >
< div class = "detail-header" >
< a href = "#hit_rate/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > hit_rate(cache_name \\ :sites_by_domain)< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L159" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< / section >
< / section >
< section class = "detail" id = "merge/2" >
< span id = "merge/1" > < / span >
< div class = "detail-header" >
< a href = "#merge/2" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > merge(new_items, opts \\ [])< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L128" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > merge(new_items :: [< a href = "Plausible.Site.html#t:t/0" > Plausible.Site.t< / a > ()], opts :: < a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: :ok< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "name/0" >
< div class = "detail-header" >
< a href = "#name/0" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > name()< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L64" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > name() :: < a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > atom< / a > ()< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "ready?/1" >
< span id = "ready?/0" > < / span >
< div class = "detail-header" >
< a href = "#ready?/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > ready?(cache_name \\ :sites_by_domain)< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L83" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > ready?(< a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > atom< / a > ()) :: < a href = "https://hexdocs.pm/elixir/typespecs.html#built-in-types" > boolean< / a > ()< / pre >
< / div >
< p > Ensures the cache has non-zero size unless no sites exist.
Useful for orchestrating app startup to prevent the service
going up asynchronously with an empty cache.< / p >
< / section >
< / section >
< section class = "detail" id = "refresh_all/1" >
< span id = "refresh_all/0" > < / span >
< div class = "detail-header" >
< a href = "#refresh_all/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > refresh_all(opts \\ [])< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L94" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > refresh_all(< a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: :ok< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "refresh_updated_recently/1" >
< span id = "refresh_updated_recently/0" > < / span >
< div class = "detail-header" >
< a href = "#refresh_updated_recently/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > refresh_updated_recently(opts \\ [])< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L103" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > refresh_updated_recently(< a href = "https://hexdocs.pm/elixir/Keyword.html#t:t/0" > Keyword.t< / a > ()) :: :ok< / pre >
< / div >
< / section >
< / section >
< section class = "detail" id = "size/1" >
< span id = "size/0" > < / span >
< div class = "detail-header" >
< a href = "#size/1" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > size(cache_name \\ :sites_by_domain)< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L153" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< / section >
< / section >
< section class = "detail" id = "telemetry_event_refresh/2" >
< span id = "telemetry_event_refresh/1" > < / span >
< div class = "detail-header" >
< a href = "#telemetry_event_refresh/2" class = "detail-link" title = "Link to this function" >
< i class = "ri-link-m" aria-hidden = "true" > < / i >
< span class = "sr-only" > Link to this function< / span >
< / a >
< h1 class = "signature" translate = "no" > telemetry_event_refresh(cache_name \\ :sites_by_domain, mode)< / h1 >
< a href = "https://github.com/plausible/analytics/blob/main/lib/plausible/site/cache.ex#L208" class = "icon-action" rel = "help" title = "View Source" >
< i class = "ri-code-s-slash-line" aria-hidden = "true" > < / i >
< span class = "sr-only" > View Source< / span >
< / a >
< / div >
< section class = "docstring" >
< div class = "specs" >
< pre translate = "no" > < span class = "attribute" > @spec< / span > telemetry_event_refresh(< a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > atom< / a > (), < a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > atom< / a > ()) :: [< a href = "https://hexdocs.pm/elixir/typespecs.html#basic-types" > atom< / a > ()]< / pre >
< / div >
< / section >
< / section >
< / div >
< / section >
< footer class = "footer" >
< p >
< span class = "line" >
< button class = "a-main footer-button display-quick-switch" title = "Search HexDocs packages" >
Search HexDocs
< / button >
< a href = "Plausible.epub" title = "ePub version" >
Download ePub version
< / a >
< / span >
< / p >
< p class = "built-using" >
Built using
2023-07-25 10:58:48 +03:00
< a href = "https://github.com/elixir-lang/ex_doc" title = "ExDoc" target = "_blank" rel = "help noopener" translate = "no" > ExDoc< / a > (v0.30.3) for the
2023-07-12 11:28:07 +03:00
< a href = "https://elixir-lang.org" title = "Elixir" target = "_blank" translate = "no" > Elixir programming language< / a >
< / p >
< / footer >
< / div >
< / div >
< / section >
< / div >
< script src = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js" > < / script >
< script > mermaid . initialize ( { startOnLoad : true } ) < / script >
< / body >
< / html >