diff --git a/404.html b/404.html new file mode 100644 index 0000000..f8414f0 --- /dev/null +++ b/404.html @@ -0,0 +1,3 @@ + +404 Not Found +

404 Not Found

diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..91e2ace --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +devnotes.tramline.app diff --git a/atom.xml b/atom.xml new file mode 100644 index 0000000..73dbfb0 --- /dev/null +++ b/atom.xml @@ -0,0 +1,374 @@ + + + + + + Zola + 2023-08-08T19:41:37+00:00 + https://devnotes.tramline.app/atom.xml + + + 2023-08-08T19:41:37+00:00 + 2023-08-08T19:41:37+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2023-08-08t19-41-37/ + + <p><code>then</code> in <a href="https://apidock.com/ruby/v2_6_3/Object/then">Ruby</a> feels like a little sister to the <code>as-&gt;</code> <a href="https://clojuredocs.org/clojure.core/as-%3E">macro</a> from clojure.</p> +<p>Most of the API code (and several other parts) in Tramline heavily make use of <code>then</code></p> +<p><a href="https://github.com/tramlinehq/tramline/blob/main/app/libs/installations/github/api.rb#L20">example</a>, <a href="https://github.com/tramlinehq/tramline/blob/main/app/libs/installations/gitlab/api.rb#L62">example</a>, <a href="https://github.com/tramlinehq/tramline/blob/main/app/libs/installations/google/firebase/api.rb#L49">example</a>.</p> +<p>There's a couple of interesting things of note here.</p> +<p>One, I think <a href="https://ruby-doc.org/core-2.7.0/Proc.html#class-Proc-label-Numbered+parameters">numbered params</a> have a lot of scope in removing some of the block cruft and overnaming of things across <code>then</code> blocks. Especially if the next in chain is visually obvious.</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#f6aa11;">@</span><span style="color:#d0d0ff;">client</span><span>.workflows(repo) +</span><span> .then { fetch_workflows(</span><span style="color:#cc7833;">*</span><span style="color:#a5c261;">1</span><span>) } +</span><span> .then { pick_active(</span><span style="color:#cc7833;">*</span><span style="color:#a5c261;">1</span><span>) } +</span><span> .then { transform_keys(</span><span style="color:#cc7833;">*</span><span style="color:#a5c261;">1</span><span>) } +</span></code></pre> +<p>The above feels more natural and less noisy than naming each intermediate step with similar sounding variable names.</p> +<p>The second one is a controversial (perhaps even wrong) point. It seems to me that since <code>then</code> is just a function, the general debuggability of something going wrong in the pipeline is easier to find out in the chain.</p> +<p>My (now fading) experience with threading macro debuggability has been less efficient; I end up adding taps and spies to figure out what part of the chain broke.</p> +<p>I prefer an experience like:</p> +<ol> +<li>write a pipeline</li> +<li>pipeline breaks in expression number 2</li> +<li>errors output nudges you clearly in the direction of just tweaking expression number 2</li> +<li>make the fix and the pipeline starts working again</li> +</ol> + + + + + + 2023-01-18T13:41:44+00:00 + 2023-01-18T13:41:44+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2023-01-18t13-41-44/ + + <p>All these years of using Rails, I had no idea <code>has_one</code> did this.</p> +<p><img src="/images/has_one.png" alt="" /></p> +<p>More importantly with a relationship like follows,</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#86c20e;">class </span><span style="text-decoration:underline;color:#ffc66d;">Model </span><span style="color:#86c20e;">&lt; </span><span style="text-decoration:underline;font-style:italic;color:#ffc66d;">ActiveRecord +</span><span> has_one </span><span style="color:#f6f080;">:build +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span style="color:#86c20e;">class </span><span style="text-decoration:underline;color:#ffc66d;">Build </span><span style="color:#86c20e;">&lt; </span><span style="text-decoration:underline;font-style:italic;color:#ffc66d;">ActiveRecord +</span><span> has_one_attached_file </span><span style="color:#f6f080;">:file +</span><span style="font-weight:bold;color:#86c20e;">end +</span></code></pre> +<p>If I <code>create_build</code> on <code>Model</code> if one already exists, it will:</p> +<ol> +<li>Delete that <code>Build</code>, create a new one and attach it to <code>Model</code></li> +<li>Kickoff an <code>ActiveStorage::PurgeJob</code> and delete the file from GCS/AWS</li> +</ol> +<p>This is highly uncomfortable.</p> + + + + + + 2023-01-10T14:58:17+00:00 + 2023-01-10T14:58:17+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2023-01-10t14-58-17/ + + <p>I have been using <code>return</code> a fair bit in transactions in Rails. In 7, these rollback the transaction and I use them as so.</p> +<p>They obviously require very careful writing and to <strong>remember</strong> that the return actually just doesn't jump out of the block, it cancels the transaction.</p> +<p>I prefer the fact that <code>return</code> from 8 will throw an exception, but I've found that using <code>Github::Result</code> around transactions as a pattern comes pretty close or is better (in some cases) already.</p> +<p>Consider this example code that creates and merges a pull request,</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">create_and_merge! +</span><span> </span><span style="color:#cc7833;">return </span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">ok?: </span><span style="color:#ae81ff;">false</span><span>) </span><span style="font-weight:bold;color:#86c20e;">unless</span><span> create.ok? +</span><span> upserted_pull_request </span><span style="color:#cc7833;">= +</span><span> </span><span style="color:#f6aa11;">@</span><span style="color:#d0d0ff;">new_pull_request</span><span>.update_or_insert!(create.value) +</span><span> +</span><span> transaction </span><span style="color:#cc7833;">do +</span><span> upserted_pull_request.close! </span><span style="color:#95815e;"># close the PR +</span><span> +</span><span> </span><span style="font-weight:bold;color:#86c20e;">if</span><span> merge.ok? +</span><span> </span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">ok?: </span><span style="color:#ae81ff;">true</span><span>) +</span><span> </span><span style="font-weight:bold;color:#86c20e;">else +</span><span> </span><span style="color:#cc7833;">return </span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">ok?: </span><span style="color:#ae81ff;">false</span><span>, </span><span style="color:#f6f080;">error: </span><span style="color:#f92672;">&quot;</span><span style="color:#c1be91;">Failed!</span><span style="color:#f92672;">&quot;</span><span>) +</span><span> </span><span style="font-weight:bold;color:#86c20e;">end +</span><span> </span><span style="font-weight:bold;color:#86c20e;">end +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">create +</span><span> </span><span style="color:#95815e;"># creates a PR and returns a Result +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">merge +</span><span> </span><span style="color:#95815e;"># merges a PR and returns a Result +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span>Result </span><span style="color:#cc7833;">= </span><span style="font-style:italic;color:#6e9cbe;">Struct</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">:ok?</span><span>, </span><span style="color:#f6f080;">:error</span><span>, </span><span style="color:#f6f080;">:value</span><span>, </span><span style="color:#f6f080;">keyword_init: </span><span style="color:#ae81ff;">true</span><span>) +</span></code></pre> +<p>One problem here is that we're using custom <code>Result</code> objects which are not very chainable. But the other more shape-y problem is that we're having to check the output from <code>merge</code>, return an ok-<code>Result</code> or else <strong>cancel</strong> the transaction and <strong>then</strong> return a not-ok-<code>Result</code>. This not only feels like excessive work but also the use of <code>return</code> is unfortunate to essentially carry out a rollback + value type scenario.</p> +<p>With <code>Github::Result</code> we can rewrite it much more cleanly,</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">create_and_merge! +</span><span> </span><span style="color:#cc7833;">return </span><span style="font-style:italic;color:#6e9cbe;">GitHub</span><span>::</span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new </span><span>{ </span><span style="color:#86c20e;">raise </span><span style="color:#6e9cbe;">CreateError </span><span>} </span><span style="font-weight:bold;color:#86c20e;">unless</span><span> create.ok? +</span><span> upserted_pull_request </span><span style="color:#cc7833;">= +</span><span> </span><span style="color:#f6aa11;">@</span><span style="color:#d0d0ff;">new_pull_request</span><span>.update_or_insert!(create.value!) +</span><span> +</span><span> </span><span style="font-style:italic;color:#6e9cbe;">GitHub</span><span>::</span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new </span><span style="color:#cc7833;">do +</span><span> transaction </span><span style="color:#cc7833;">do +</span><span> upserted_pull_request.close! </span><span style="color:#95815e;"># close the PR +</span><span> merge.value! +</span><span> </span><span style="font-weight:bold;color:#86c20e;">end +</span><span> </span><span style="font-weight:bold;color:#86c20e;">end +</span><span style="font-weight:bold;color:#86c20e;">end +</span></code></pre> +<p>As long as <code>merge</code> throws an exception, <code>value!</code> will raise it, rollback (throwing an exception will rollback) and opaquely pass it further up to the wrappper <code>Result</code>. This allows us to avoid <code>return</code> magic and ugly raises in the middle of the transaction block and chain the exceptions up.</p> + + + + + + 2023-01-06T14:36:17+00:00 + 2023-01-06T14:36:17+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2023-01-06t14-36-17/ + + <p><img src="/images/txns-suck.png" alt="" /></p> +<p>This is terrible.</p> + + + + + + 2023-01-06T14:25:59+00:00 + 2023-01-06T14:25:59+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2023-01-06t14-25-59/ + + <p>I decided to add stricter linting to the codebase. Since I like what standard offers, I simply extended it with rails specific lints, in the following way:</p> +<pre data-lang="yaml" style="background-color:#383838;color:#e6e1dc;" class="language-yaml "><code class="language-yaml" data-lang="yaml"><span style="color:#e8bf6a;">require</span><span>: +</span><span> - </span><span style="color:#a5c261;">standard +</span><span> - </span><span style="color:#a5c261;">rubocop-rails +</span><span> - </span><span style="color:#a5c261;">rubocop-rspec +</span><span> - </span><span style="color:#a5c261;">rubocop-performance +</span><span> +</span><span style="color:#e8bf6a;">inherit_gem</span><span>: +</span><span> </span><span style="color:#e8bf6a;">standard</span><span>: </span><span style="color:#a5c261;">config/ruby-3.0.yml +</span><span> +</span><span style="color:#e8bf6a;">AllCops</span><span>: +</span><span> </span><span style="color:#e8bf6a;">TargetRubyVersion</span><span>: </span><span style="color:#a5c261;">3.1 +</span><span> </span><span style="color:#e8bf6a;">NewCops</span><span>: </span><span style="color:#a5c261;">enable +</span><span> </span><span style="color:#e8bf6a;">Exclude</span><span>: +</span><span> - </span><span style="color:#a5c261;">bin/**/* +</span><span> - </span><span style="color:#a5c261;">public/**/* +</span><span> - </span><span style="color:#a5c261;">vendor/**/* +</span><span> - </span><span style="color:#a5c261;">db/schema.rb +</span><span> +</span><span style="color:#e8bf6a;">Rails</span><span>: +</span><span> </span><span style="color:#e8bf6a;">Enabled</span><span>: </span><span style="color:#6e9cbe;">true +</span><span> +</span><span style="color:#e8bf6a;">RSpec</span><span>: +</span><span> </span><span style="color:#e8bf6a;">Enabled</span><span>: </span><span style="color:#6e9cbe;">true +</span><span> +</span><span style="color:#e8bf6a;">RSpec/ExampleLength</span><span>: +</span><span> </span><span style="color:#e8bf6a;">Enabled</span><span>: </span><span style="color:#6e9cbe;">false +</span><span> +</span><span style="color:#e8bf6a;">RSpec/MultipleExpectations</span><span>: +</span><span> </span><span style="color:#e8bf6a;">Max</span><span>: </span><span style="color:#a5c261;">4 +</span><span> +</span><span style="color:#e8bf6a;">Performance</span><span>: +</span><span> </span><span style="color:#e8bf6a;">Enabled</span><span>: </span><span style="color:#6e9cbe;">true +</span></code></pre> +<p>This ensures standard is used for ruby things, and rails, rspec and some other performance related checks in addition to it via rubocop.</p> +<p>I came across this suggestion while fixing the 100 odd offenses it threw up,</p> +<pre style="background-color:#383838;color:#e6e1dc;"><code><span>RSpec/NamedSubject: +</span><span> Name your test subject if you need to reference it explicitly. +</span></code></pre> +<p>The fix,</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>subject(</span><span style="color:#f6f080;">:run</span><span>) { create(</span><span style="color:#f6f080;">:releases_train_run</span><span>) } +</span></code></pre> +<p>But what even is this? Isn't this just a <code>let</code> really? You can't really use the <code>is_expected.to</code> shortcut either since it's now named. I cannot possibly make the claim that this reads better.</p> +<p>I think it's just one of those annoying rspec maximalisms.</p> + + + + + + 2022-10-11T15:44:45+00:00 + 2022-10-11T15:44:45+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-10-11t15-44-45/ + + <p>Here's a spectrum of componentizing views I've been thinking about lately (in Rails):</p> +<ol> +<li>Extract logical helpers and partials</li> +</ol> +<p>They are often like stuffing away code inside a <code>Module</code> and calling it a refactor. But it is cheap and effective enough to start off with. You often run into &quot;too many allocations&quot; issues, with nested views and partials. They also don't abstract state very well. Arguments for their limitations have been made ad naseam - <a href="https://github.com/drapergem/draper">ex</a>.</p> +<pre data-lang="text" style="background-color:#383838;color:#e6e1dc;" class="language-text "><code class="language-text" data-lang="text"><span>Completed 200 OK in 86ms (Views: 83.6ms | ActiveRecord: 1.3ms | Allocations: 29347) +</span></code></pre> +<ol start="2"> +<li>Presenter objects / ViewComponents</li> +</ol> +<p>Solid ideas here. Fair bit of upfront work required and you have to be meticulous about the granularity of your components. These claim to be very fast by precompiling templates at rails boot.</p> +<ol start="3"> +<li>SPAs, building JSX components (or whatever else is out there now)</li> +</ol> +<p>I'm not convinced I'd need to do this. I think Stimulus / Hotwired is powerful enough for all our needs for the forseeable future (famous last words). I still remember the joy of ripping out all the react from a <a href="https://github.com/simpledotorg/simple-server">previous codebase</a> I worked on and rewrote them in a much simpler, basic markup and js.</p> +<p>#1 ➡ #2 hopefuly in a few months time.</p> + + + + + + 2022-10-06T12:23:54+00:00 + 2022-10-06T12:23:54+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-10-06t12-23-54/ + + <p>When I originally kicked things off, I added a lot of big transactions around various api-call-surrounding-database-ops. This was knowingly a cheap hack to avoid bad state changes and delaying the inevitable: careful and tedious handling of errors and control-flow.</p> +<p>I'm now slowly refactoring things to unpack these big transactions and I realize many of them are actually unnecessary. I'm employing yet another cheap <code>Result</code> object to signify boundary-states,</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>Result </span><span style="color:#cc7833;">= </span><span style="font-style:italic;color:#6e9cbe;">Struct</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">:ok?</span><span>, </span><span style="color:#f6f080;">:error</span><span>, </span><span style="color:#f6f080;">:value</span><span>, </span><span style="color:#f6f080;">keyword_init: </span><span style="color:#ae81ff;">true</span><span>) +</span><span> +</span><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">good_op +</span><span> </span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">ok?: </span><span style="color:#ae81ff;">true</span><span>, </span><span style="color:#f6f080;">value: </span><span style="color:#a5c261;">1</span><span>) +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span style="font-weight:bold;color:#86c20e;">def </span><span style="color:#f6aa11;">bad_op +</span><span> </span><span style="font-style:italic;color:#6e9cbe;">Result</span><span>.</span><span style="color:#86c20e;">new</span><span>(</span><span style="color:#f6f080;">ok?: </span><span style="color:#ae81ff;">false</span><span>, </span><span style="color:#f6f080;">error: </span><span style="color:#f92672;">&quot;</span><span style="color:#c1be91;">Did not work</span><span style="color:#f92672;">&quot;</span><span>) +</span><span style="font-weight:bold;color:#86c20e;">end +</span><span> +</span><span>operation.ok? +</span><span>operation.value +</span><span>operation.error +</span></code></pre> +<p>This is a little bit silly, because it requires <em>you</em> to ensure that <code>value</code> is only present alongside the <code>ok?</code> and the others are falsey in the <code>error</code> case. But it is very simple and quick to get a refactor busted out.</p> +<p>I have used <a href="https://github.com/github/github-ds#githubresult">Github::Result</a> in the past, but recently discovered <a href="https://dry-rb.org/gems/dry-monads/1.3">dry-rb</a> -- many of these look very sensible and immediately useful. Hopefully the next step!</p> + + + + + + 2022-10-05T00:19:39+00:00 + 2022-10-05T00:19:39+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-10-05t00-19-39/ + + <p><img src="/images/rubymine.png" alt="" /></p> + + + + + + 2022-09-28T11:15:40+00:00 + 2022-09-28T11:15:40+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-09-28t11-15-40/ + + <p>There are a few tight, but rare-enough race-conditions lingering about and they are constantly bothering me. They just exist as <code>FIXMEs</code> in the code atm.</p> +<p>Most of the these seem to be solvable by pessimistic row-locks, but I'm already dreading the littering of necessary but unintuitive placements of various row-locks throughout the code-base.</p> +<p>Would modelling this as a pure declarative orchestrator help keep this in check?</p> +<p>Not sure.</p> + + + + + + 2022-09-28T11:15:39+00:00 + 2022-09-28T11:15:39+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-09-28t11-15-39/ + + <p>The views currently are a hot mess, giant nested ERB templates. +I'm not convinced of jumping into <a href="https://viewcomponent.org">ViewComponents</a> yet, since the shape of this UI is so nebulous and will definitely change dramatically and I do not want to deal with more objects and more abstractions and more code at the moment.</p> +<p>I'll deal with the complex markup like it's 1999.</p> + + + + + + 2022-09-28T09:02:54+00:00 + 2022-09-28T09:02:54+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-09-28t09-02-54/ + + <p>It's not terribly hard to come up with a state-machine DSL like <a href="https://github.com/aasm/aasm">aasm</a> +by hand, but the <code>ActiveRecord</code> support is very handy.</p> +<p>I'm using it with an <code>enum</code> against a <code>status</code> field with the following options:</p> +<pre data-lang="ruby" style="background-color:#383838;color:#e6e1dc;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>{ +</span><span> </span><span style="color:#f6f080;">column: :status</span><span>, +</span><span> </span><span style="color:#f6f080;">requires_lock: </span><span style="color:#ae81ff;">true</span><span>, +</span><span> </span><span style="color:#f6f080;">requires_new_transaction: </span><span style="color:#ae81ff;">false</span><span>, +</span><span> </span><span style="color:#f6f080;">enum: </span><span style="color:#ae81ff;">true</span><span>, +</span><span> </span><span style="color:#f6f080;">create_scopes: </span><span style="color:#ae81ff;">false +</span><span>} +</span></code></pre> +<p>The <code>requires_new_transaction</code> is <code>false</code> to avoid landing into unnecessary nested transactions. I'll keep my transactions explicit and intentional. +The <code>enum</code> allows me to play well with the Rails enum which already generates scopes, so I keep <code>create_scopes</code> off.</p> +<p>The <code>requires_lock</code> is seemingly quite handy at first glance, since I require locking rows a fair bit because of the distributed nature of the app. +But the level at which it locks is far too granular. I generally seem to require locking rows for far longer and more work than simply transitioning state and not everything can be sanely encapsulated in a <code>before</code> or <code>after</code>. +I'd rather provide the library with methods that cause a state transition than the inverted way it currently operates: generating methods to transition for you and then locking within that event.</p> + + + + + + 2022-09-26T11:37:01+00:00 + 2022-09-26T11:37:01+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-09-26t11-37-01/ + + <p>I'm finding keeping POROs seperated away from <code>models/</code> a mental relief for some reason. I think I'm going to stick to it.</p> +<p><img src="/images/dir.png" alt="" /></p> + + + + + + 2022-09-25T21:18:31+00:00 + 2022-09-25T21:18:31+00:00 + + Unknown + + + https://devnotes.tramline.app/post-2022-09-25t21-18-31/ + + <p>The log streams feature in <a href="https://render.com">Render</a> is a little bit strange. From the <a href="https://render.com/docs/log-streams#configuring-log-streams">docs</a>,</p> +<pre style="background-color:#383838;color:#e6e1dc;"><code><span>Render Log Streams forward logs from your web services, +</span><span>private services, background workers, databases, +</span><span>and cron jobs to any logging provider that supplies a TLS-enabled syslog drain. +</span></code></pre> +<p>I hooked this up with datadog and it is indeed a syslog drain across <em>all</em> services. This means my newly setup datadog is now filled with thousands of non-application related logs. Not only now do I have to filter all these out, but also pay for them.</p> +<p>Surely a log streaming pipeline should have a minimum of a service-level (if not content-level) filtering as baseline features?</p> + + + + diff --git a/elasticlunr.min.js b/elasticlunr.min.js new file mode 100644 index 0000000..79dad65 --- /dev/null +++ b/elasticlunr.min.js @@ -0,0 +1,10 @@ +/** + * elasticlunr - http://weixsong.github.io + * Lightweight full-text search engine in Javascript for browser search and offline search. - 0.9.6 + * + * Copyright (C) 2017 Oliver Nightingale + * Copyright (C) 2017 Wei Song + * MIT Licensed + * @license + */ +!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ +
+ + +
+
+
+
+
+
+
2023 / Aug 08 — 19:41
+
kitallis
+ + + + + + + + +
+
+
+
+
+

then in Ruby feels like a little sister to the as-> macro from clojure.

+

Most of the API code (and several other parts) in Tramline heavily make use of then

+

example, example, example.

+

There's a couple of interesting things of note here.

+

One, I think numbered params have a lot of scope in removing some of the block cruft and overnaming of things across then blocks. Especially if the next in chain is visually obvious.

+
@client.workflows(repo)
+       .then { fetch_workflows(*1) }
+       .then { pick_active(*1) }
+       .then { transform_keys(*1) }
+
+

The above feels more natural and less noisy than naming each intermediate step with similar sounding variable names.

+

The second one is a controversial (perhaps even wrong) point. It seems to me that since then is just a function, the general debuggability of something going wrong in the pipeline is easier to find out in the chain.

+

My (now fading) experience with threading macro debuggability has been less efficient; I end up adding taps and spies to figure out what part of the chain broke.

+

I prefer an experience like:

+
    +
  1. write a pipeline
  2. +
  3. pipeline breaks in expression number 2
  4. +
  5. errors output nudges you clearly in the direction of just tweaking expression number 2
  6. +
  7. make the fix and the pipeline starts working again
  8. +
+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2023 / Jan 18 — 13:41
+
kitallis
+ + + + + + + + +
+
+
+
+
+

All these years of using Rails, I had no idea has_one did this.

+

+

More importantly with a relationship like follows,

+
class Model < ActiveRecord
+  has_one :build
+end
+
+class Build < ActiveRecord
+  has_one_attached_file :file
+end
+
+

If I create_build on Model if one already exists, it will:

+
    +
  1. Delete that Build, create a new one and attach it to Model
  2. +
  3. Kickoff an ActiveStorage::PurgeJob and delete the file from GCS/AWS
  4. +
+

This is highly uncomfortable.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2023 / Jan 10 — 14:58
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I have been using return a fair bit in transactions in Rails. In 7, these rollback the transaction and I use them as so.

+

They obviously require very careful writing and to remember that the return actually just doesn't jump out of the block, it cancels the transaction.

+

I prefer the fact that return from 8 will throw an exception, but I've found that using Github::Result around transactions as a pattern comes pretty close or is better (in some cases) already.

+

Consider this example code that creates and merges a pull request,

+
def create_and_merge!
+  return Result.new(ok?: false) unless create.ok?
+  upserted_pull_request =
+    @new_pull_request.update_or_insert!(create.value)
+
+  transaction do
+    upserted_pull_request.close! # close the PR
+
+    if merge.ok?
+      Result.new(ok?: true)
+    else
+      return Result.new(ok?: false, error: "Failed!")
+    end
+  end
+end
+
+def create
+  # creates a PR and returns a Result
+end
+
+def merge
+  # merges a PR and returns a Result
+end
+
+Result = Struct.new(:ok?, :error, :value, keyword_init: true)
+
+

One problem here is that we're using custom Result objects which are not very chainable. But the other more shape-y problem is that we're having to check the output from merge, return an ok-Result or else cancel the transaction and then return a not-ok-Result. This not only feels like excessive work but also the use of return is unfortunate to essentially carry out a rollback + value type scenario.

+

With Github::Result we can rewrite it much more cleanly,

+
def create_and_merge!
+   return GitHub::Result.new { raise CreateError } unless create.ok?
+   upserted_pull_request =
+     @new_pull_request.update_or_insert!(create.value!)
+
+   GitHub::Result.new do
+     transaction do
+       upserted_pull_request.close! # close the PR
+       merge.value!
+     end
+  end
+end
+
+

As long as merge throws an exception, value! will raise it, rollback (throwing an exception will rollback) and opaquely pass it further up to the wrappper Result. This allows us to avoid return magic and ugly raises in the middle of the transaction block and chain the exceptions up.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2023 / Jan 06 — 14:36
+
kitallis
+ + + + + + + + +
+
+
+
+
+

+

This is terrible.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2023 / Jan 06 — 14:25
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I decided to add stricter linting to the codebase. Since I like what standard offers, I simply extended it with rails specific lints, in the following way:

+
require:
+  - standard
+  - rubocop-rails
+  - rubocop-rspec
+  - rubocop-performance
+
+inherit_gem:
+  standard: config/ruby-3.0.yml
+
+AllCops:
+  TargetRubyVersion: 3.1
+  NewCops: enable
+  Exclude:
+    - bin/**/*
+    - public/**/*
+    - vendor/**/*
+    - db/schema.rb
+
+Rails:
+  Enabled: true
+
+RSpec:
+  Enabled: true
+
+RSpec/ExampleLength:
+  Enabled: false
+
+RSpec/MultipleExpectations:
+  Max: 4
+
+Performance:
+  Enabled: true
+
+

This ensures standard is used for ruby things, and rails, rspec and some other performance related checks in addition to it via rubocop.

+

I came across this suggestion while fixing the 100 odd offenses it threw up,

+
RSpec/NamedSubject:
+  Name your test subject if you need to reference it explicitly.
+
+

The fix,

+
subject(:run) { create(:releases_train_run) }
+
+

But what even is this? Isn't this just a let really? You can't really use the is_expected.to shortcut either since it's now named. I cannot possibly make the claim that this reads better.

+

I think it's just one of those annoying rspec maximalisms.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Oct 11 — 15:44
+
kitallis
+ + + + + + + + +
+
+
+
+
+

Here's a spectrum of componentizing views I've been thinking about lately (in Rails):

+
    +
  1. Extract logical helpers and partials
  2. +
+

They are often like stuffing away code inside a Module and calling it a refactor. But it is cheap and effective enough to start off with. You often run into "too many allocations" issues, with nested views and partials. They also don't abstract state very well. Arguments for their limitations have been made ad naseam - ex.

+
Completed 200 OK in 86ms (Views: 83.6ms | ActiveRecord: 1.3ms | Allocations: 29347)
+
+
    +
  1. Presenter objects / ViewComponents
  2. +
+

Solid ideas here. Fair bit of upfront work required and you have to be meticulous about the granularity of your components. These claim to be very fast by precompiling templates at rails boot.

+
    +
  1. SPAs, building JSX components (or whatever else is out there now)
  2. +
+

I'm not convinced I'd need to do this. I think Stimulus / Hotwired is powerful enough for all our needs for the forseeable future (famous last words). I still remember the joy of ripping out all the react from a previous codebase I worked on and rewrote them in a much simpler, basic markup and js.

+

#1 ➡ #2 hopefuly in a few months time.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Oct 06 — 12:23
+
kitallis
+ + + + + + + + +
+
+
+
+
+

When I originally kicked things off, I added a lot of big transactions around various api-call-surrounding-database-ops. This was knowingly a cheap hack to avoid bad state changes and delaying the inevitable: careful and tedious handling of errors and control-flow.

+

I'm now slowly refactoring things to unpack these big transactions and I realize many of them are actually unnecessary. I'm employing yet another cheap Result object to signify boundary-states,

+
Result = Struct.new(:ok?, :error, :value, keyword_init: true)
+
+def good_op
+  Result.new(ok?: true, value: 1)
+end
+
+def bad_op
+  Result.new(ok?: false, error: "Did not work")
+end
+
+operation.ok?
+operation.value
+operation.error
+
+

This is a little bit silly, because it requires you to ensure that value is only present alongside the ok? and the others are falsey in the error case. But it is very simple and quick to get a refactor busted out.

+

I have used Github::Result in the past, but recently discovered dry-rb -- many of these look very sensible and immediately useful. Hopefully the next step!

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Oct 05 — 00:19
+
kitallis
+ + + + + + + + +
+
+
+
+
+

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Sep 28 — 11:15
+
kitallis
+ + + + + + + + +
+
+
+
+
+

There are a few tight, but rare-enough race-conditions lingering about and they are constantly bothering me. They just exist as FIXMEs in the code atm.

+

Most of the these seem to be solvable by pessimistic row-locks, but I'm already dreading the littering of necessary but unintuitive placements of various row-locks throughout the code-base.

+

Would modelling this as a pure declarative orchestrator help keep this in check?

+

Not sure.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Sep 28 — 11:15
+
kitallis
+ + + + + + + + +
+
+
+
+
+

The views currently are a hot mess, giant nested ERB templates. +I'm not convinced of jumping into ViewComponents yet, since the shape of this UI is so nebulous and will definitely change dramatically and I do not want to deal with more objects and more abstractions and more code at the moment.

+

I'll deal with the complex markup like it's 1999.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Sep 28 — 09:02
+
kitallis
+ + + + + + + + +
+
+
+
+
+

It's not terribly hard to come up with a state-machine DSL like aasm +by hand, but the ActiveRecord support is very handy.

+

I'm using it with an enum against a status field with the following options:

+
{
+  column: :status,
+  requires_lock: true,
+  requires_new_transaction: false,
+  enum: true,
+  create_scopes: false
+}
+
+

The requires_new_transaction is false to avoid landing into unnecessary nested transactions. I'll keep my transactions explicit and intentional. +The enum allows me to play well with the Rails enum which already generates scopes, so I keep create_scopes off.

+

The requires_lock is seemingly quite handy at first glance, since I require locking rows a fair bit because of the distributed nature of the app. +But the level at which it locks is far too granular. I generally seem to require locking rows for far longer and more work than simply transitioning state and not everything can be sanely encapsulated in a before or after. +I'd rather provide the library with methods that cause a state transition than the inverted way it currently operates: generating methods to transition for you and then locking within that event.

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Sep 26 — 11:37
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I'm finding keeping POROs seperated away from models/ a mental relief for some reason. I think I'm going to stick to it.

+

+ +
+
+
+
+
+ + + +
+
+
+
+
+
+
2022 / Sep 25 — 21:18
+
kitallis
+ + + + + + + + +
+
+
+
+
+

The log streams feature in Render is a little bit strange. From the docs,

+
Render Log Streams forward logs from your web services,
+private services, background workers, databases,
+and cron jobs to any logging provider that supplies a TLS-enabled syslog drain.
+
+

I hooked this up with datadog and it is indeed a syslog drain across all services. This means my newly setup datadog is now filled with thousands of non-application related logs. Not only now do I have to filter all these out, but also pay for them.

+

Surely a log streaming pipeline should have a minimum of a service-level (if not content-level) filtering as baseline features?

+ +
+
+
+
+
+ + +
+ + + +
+ + diff --git a/main.js b/main.js new file mode 100644 index 0000000..a81522a --- /dev/null +++ b/main.js @@ -0,0 +1,210 @@ +function switchScheme(e) { + e.style.color = 'var(--secondary-text)'; + + window.localStorage.setItem( + 'colorscheme', + currentlyPrefersDark() ? 'light' : 'dark', + ); + + updateScheme(); + return e; +} + +function currentlyPrefersDark() { + const savedScheme = window.localStorage.getItem('colorscheme'); + + if (savedScheme != null) { + return savedScheme === 'dark'; + } + + return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; +} + +function updateScheme() { + if (currentlyPrefersDark()) { + document.body.classList.add('dark'); + document.body.classList.remove('light') + } else { + document.body.classList.add('light') + document.body.classList.remove('dark'); + } +} + +function debounce(func, wait) { + var timeout; + + return function () { + var context = this; + var args = arguments; + clearTimeout(timeout); + + timeout = setTimeout(function () { + timeout = null; + func.apply(context, args); + }, wait); + }; +} + +// Taken from mdbook +// The strategy is as follows: +// First, assign a value to each word in the document: +// Words that correspond to search terms (stemmer aware): 40 +// Normal words: 2 +// First word in a sentence: 8 +// Then use a sliding window with a constant number of words and count the +// sum of the values of the words within the window. Then use the window that got the +// maximum sum. If there are multiple maximas, then get the last one. +// Enclose the terms in . +function makeTeaser(body, terms) { + var TERM_WEIGHT = 40; + var NORMAL_WORD_WEIGHT = 2; + var FIRST_WORD_WEIGHT = 8; + var TEASER_MAX_WORDS = 30; + + var stemmedTerms = terms.map(function (w) { + return elasticlunr.stemmer(w.toLowerCase()); + }); + var termFound = false; + var index = 0; + var weighted = []; // contains elements of ["word", weight, index_in_document] + + // split in sentences, then words + var sentences = body.toLowerCase().split(". "); + + for (var i in sentences) { + var words = sentences[i].split(" "); + var value = FIRST_WORD_WEIGHT; + + for (var j in words) { + var word = words[j]; + + if (word.length > 0) { + for (var k in stemmedTerms) { + if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { + value = TERM_WEIGHT; + termFound = true; + } + } + weighted.push([word, value, index]); + value = NORMAL_WORD_WEIGHT; + } + + index += word.length; + index += 1; // ' ' or '.' if last word in sentence + } + + index += 1; // because we split at a two-char boundary '. ' + } + + if (weighted.length === 0) { + return body; + } + + var windowWeights = []; + var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); + // We add a window with all the weights first + var curSum = 0; + for (var i = 0; i < windowSize; i++) { + curSum += weighted[i][1]; + } + windowWeights.push(curSum); + + for (var i = 0; i < weighted.length - windowSize; i++) { + curSum -= weighted[i][1]; + curSum += weighted[i + windowSize][1]; + windowWeights.push(curSum); + } + + // If we didn't find the term, just pick the first window + var maxSumIndex = 0; + if (termFound) { + var maxFound = 0; + // backwards + for (var i = windowWeights.length - 1; i >= 0; i--) { + if (windowWeights[i] > maxFound) { + maxFound = windowWeights[i]; + maxSumIndex = i; + } + } + } + + var teaser = []; + var startIndex = weighted[maxSumIndex][2]; + for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { + var word = weighted[i]; + if (startIndex < word[2]) { + // missing text from index to start of `word` + teaser.push(body.substring(startIndex, word[2])); + startIndex = word[2]; + } + + // add around search terms + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + startIndex = word[2] + word[0].length; + teaser.push(body.substring(word[2], startIndex)); + + if (word[1] === TERM_WEIGHT) { + teaser.push(""); + } + } + teaser.push("…"); + return teaser.join(""); +} + +function formatSearchResultItem(item, terms) { + return '
' + + `${item.doc.title}` + + `
${makeTeaser(item.doc.body, terms)}
` + + '
'; +} + +function initSearch() { + var $searchInput = document.getElementById("search"); + var $searchResults = document.querySelector(".search-results"); + var $searchResultsItems = document.querySelector(".search-results__items"); + var MAX_ITEMS = 10; + + var options = { + bool: "AND", + fields: { + title: {boost: 2}, + body: {boost: 1}, + } + }; + var currentTerm = ""; + var index = elasticlunr.Index.load(window.searchIndex); + + $searchInput.addEventListener("keyup", debounce(function() { + var term = $searchInput.value.trim(); + if (term === currentTerm || !index) { + return; + } + $searchResults.style.display = term === "" ? "none" : "block"; + $searchResultsItems.innerHTML = ""; + currentTerm = term; + if (term === "") { + return; + } + + var results = index.search(term, options); + if (results.length === 0) { + $searchResults.style.display = "none"; + return; + } + + for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) { + var item = document.createElement("li"); + item.innerHTML = formatSearchResultItem(results[i], term.split(" ")); + $searchResultsItems.appendChild(item); + } + }, 150)); + + window.addEventListener('click', function(e) { + if ($searchResults.style.display == "block" && !$searchResults.contains(e.target)) { + $searchResults.style.display = "none"; + } + }); +} + diff --git a/page/1/index.html b/page/1/index.html new file mode 100644 index 0000000..c9cad55 --- /dev/null +++ b/page/1/index.html @@ -0,0 +1,6 @@ + + + + +Redirect +

Click here to be redirected.

diff --git a/post-2022-09-25t21-18-31/index.html b/post-2022-09-25t21-18-31/index.html new file mode 100644 index 0000000..3b986cf --- /dev/null +++ b/post-2022-09-25t21-18-31/index.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Sep 25 — 21:18
+
kitallis
+ + + + + + + + +
+
+
+
+
+

The log streams feature in Render is a little bit strange. From the docs,

+
Render Log Streams forward logs from your web services,
+private services, background workers, databases,
+and cron jobs to any logging provider that supplies a TLS-enabled syslog drain.
+
+

I hooked this up with datadog and it is indeed a syslog drain across all services. This means my newly setup datadog is now filled with thousands of non-application related logs. Not only now do I have to filter all these out, but also pay for them.

+

Surely a log streaming pipeline should have a minimum of a service-level (if not content-level) filtering as baseline features?

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-09-26t11-37-01/index.html b/post-2022-09-26t11-37-01/index.html new file mode 100644 index 0000000..aeaadca --- /dev/null +++ b/post-2022-09-26t11-37-01/index.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Sep 26 — 11:37
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I'm finding keeping POROs seperated away from models/ a mental relief for some reason. I think I'm going to stick to it.

+

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-09-28t09-02-54/index.html b/post-2022-09-28t09-02-54/index.html new file mode 100644 index 0000000..f0baa95 --- /dev/null +++ b/post-2022-09-28t09-02-54/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Sep 28 — 09:02
+
kitallis
+ + + + + + + + +
+
+
+
+
+

It's not terribly hard to come up with a state-machine DSL like aasm +by hand, but the ActiveRecord support is very handy.

+

I'm using it with an enum against a status field with the following options:

+
{
+  column: :status,
+  requires_lock: true,
+  requires_new_transaction: false,
+  enum: true,
+  create_scopes: false
+}
+
+

The requires_new_transaction is false to avoid landing into unnecessary nested transactions. I'll keep my transactions explicit and intentional. +The enum allows me to play well with the Rails enum which already generates scopes, so I keep create_scopes off.

+

The requires_lock is seemingly quite handy at first glance, since I require locking rows a fair bit because of the distributed nature of the app. +But the level at which it locks is far too granular. I generally seem to require locking rows for far longer and more work than simply transitioning state and not everything can be sanely encapsulated in a before or after. +I'd rather provide the library with methods that cause a state transition than the inverted way it currently operates: generating methods to transition for you and then locking within that event.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-09-28t11-15-39/index.html b/post-2022-09-28t11-15-39/index.html new file mode 100644 index 0000000..b9b66a3 --- /dev/null +++ b/post-2022-09-28t11-15-39/index.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Sep 28 — 11:15
+
kitallis
+ + + + + + + + +
+
+
+
+
+

The views currently are a hot mess, giant nested ERB templates. +I'm not convinced of jumping into ViewComponents yet, since the shape of this UI is so nebulous and will definitely change dramatically and I do not want to deal with more objects and more abstractions and more code at the moment.

+

I'll deal with the complex markup like it's 1999.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-09-28t11-15-40/index.html b/post-2022-09-28t11-15-40/index.html new file mode 100644 index 0000000..236c69c --- /dev/null +++ b/post-2022-09-28t11-15-40/index.html @@ -0,0 +1,109 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Sep 28 — 11:15
+
kitallis
+ + + + + + + + +
+
+
+
+
+

There are a few tight, but rare-enough race-conditions lingering about and they are constantly bothering me. They just exist as FIXMEs in the code atm.

+

Most of the these seem to be solvable by pessimistic row-locks, but I'm already dreading the littering of necessary but unintuitive placements of various row-locks throughout the code-base.

+

Would modelling this as a pure declarative orchestrator help keep this in check?

+

Not sure.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-10-05t00-19-39/index.html b/post-2022-10-05t00-19-39/index.html new file mode 100644 index 0000000..7e8ef1e --- /dev/null +++ b/post-2022-10-05t00-19-39/index.html @@ -0,0 +1,106 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Oct 05 — 00:19
+
kitallis
+ + + + + + + + +
+
+
+
+
+

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-10-06t12-23-54/index.html b/post-2022-10-06t12-23-54/index.html new file mode 100644 index 0000000..f8d3509 --- /dev/null +++ b/post-2022-10-06t12-23-54/index.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Oct 06 — 12:23
+
kitallis
+ + + + + + + + +
+
+
+
+
+

When I originally kicked things off, I added a lot of big transactions around various api-call-surrounding-database-ops. This was knowingly a cheap hack to avoid bad state changes and delaying the inevitable: careful and tedious handling of errors and control-flow.

+

I'm now slowly refactoring things to unpack these big transactions and I realize many of them are actually unnecessary. I'm employing yet another cheap Result object to signify boundary-states,

+
Result = Struct.new(:ok?, :error, :value, keyword_init: true)
+
+def good_op
+  Result.new(ok?: true, value: 1)
+end
+
+def bad_op
+  Result.new(ok?: false, error: "Did not work")
+end
+
+operation.ok?
+operation.value
+operation.error
+
+

This is a little bit silly, because it requires you to ensure that value is only present alongside the ok? and the others are falsey in the error case. But it is very simple and quick to get a refactor busted out.

+

I have used Github::Result in the past, but recently discovered dry-rb -- many of these look very sensible and immediately useful. Hopefully the next step!

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2022-10-11t15-44-45/index.html b/post-2022-10-11t15-44-45/index.html new file mode 100644 index 0000000..2a9c95d --- /dev/null +++ b/post-2022-10-11t15-44-45/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2022 / Oct 11 — 15:44
+
kitallis
+ + + + + + + + +
+
+
+
+
+

Here's a spectrum of componentizing views I've been thinking about lately (in Rails):

+
    +
  1. Extract logical helpers and partials
  2. +
+

They are often like stuffing away code inside a Module and calling it a refactor. But it is cheap and effective enough to start off with. You often run into "too many allocations" issues, with nested views and partials. They also don't abstract state very well. Arguments for their limitations have been made ad naseam - ex.

+
Completed 200 OK in 86ms (Views: 83.6ms | ActiveRecord: 1.3ms | Allocations: 29347)
+
+
    +
  1. Presenter objects / ViewComponents
  2. +
+

Solid ideas here. Fair bit of upfront work required and you have to be meticulous about the granularity of your components. These claim to be very fast by precompiling templates at rails boot.

+
    +
  1. SPAs, building JSX components (or whatever else is out there now)
  2. +
+

I'm not convinced I'd need to do this. I think Stimulus / Hotwired is powerful enough for all our needs for the forseeable future (famous last words). I still remember the joy of ripping out all the react from a previous codebase I worked on and rewrote them in a much simpler, basic markup and js.

+

#1 ➡ #2 hopefuly in a few months time.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2023-01-06t14-25-59/index.html b/post-2023-01-06t14-25-59/index.html new file mode 100644 index 0000000..2ba36ed --- /dev/null +++ b/post-2023-01-06t14-25-59/index.html @@ -0,0 +1,149 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2023 / Jan 06 — 14:25
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I decided to add stricter linting to the codebase. Since I like what standard offers, I simply extended it with rails specific lints, in the following way:

+
require:
+  - standard
+  - rubocop-rails
+  - rubocop-rspec
+  - rubocop-performance
+
+inherit_gem:
+  standard: config/ruby-3.0.yml
+
+AllCops:
+  TargetRubyVersion: 3.1
+  NewCops: enable
+  Exclude:
+    - bin/**/*
+    - public/**/*
+    - vendor/**/*
+    - db/schema.rb
+
+Rails:
+  Enabled: true
+
+RSpec:
+  Enabled: true
+
+RSpec/ExampleLength:
+  Enabled: false
+
+RSpec/MultipleExpectations:
+  Max: 4
+
+Performance:
+  Enabled: true
+
+

This ensures standard is used for ruby things, and rails, rspec and some other performance related checks in addition to it via rubocop.

+

I came across this suggestion while fixing the 100 odd offenses it threw up,

+
RSpec/NamedSubject:
+  Name your test subject if you need to reference it explicitly.
+
+

The fix,

+
subject(:run) { create(:releases_train_run) }
+
+

But what even is this? Isn't this just a let really? You can't really use the is_expected.to shortcut either since it's now named. I cannot possibly make the claim that this reads better.

+

I think it's just one of those annoying rspec maximalisms.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2023-01-06t14-36-17/index.html b/post-2023-01-06t14-36-17/index.html new file mode 100644 index 0000000..43610a8 --- /dev/null +++ b/post-2023-01-06t14-36-17/index.html @@ -0,0 +1,107 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2023 / Jan 06 — 14:36
+
kitallis
+ + + + + + + + +
+
+
+
+
+

+

This is terrible.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2023-01-10t14-58-17/index.html b/post-2023-01-10t14-58-17/index.html new file mode 100644 index 0000000..2bd78c1 --- /dev/null +++ b/post-2023-01-10t14-58-17/index.html @@ -0,0 +1,151 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2023 / Jan 10 — 14:58
+
kitallis
+ + + + + + + + +
+
+
+
+
+

I have been using return a fair bit in transactions in Rails. In 7, these rollback the transaction and I use them as so.

+

They obviously require very careful writing and to remember that the return actually just doesn't jump out of the block, it cancels the transaction.

+

I prefer the fact that return from 8 will throw an exception, but I've found that using Github::Result around transactions as a pattern comes pretty close or is better (in some cases) already.

+

Consider this example code that creates and merges a pull request,

+
def create_and_merge!
+  return Result.new(ok?: false) unless create.ok?
+  upserted_pull_request =
+    @new_pull_request.update_or_insert!(create.value)
+
+  transaction do
+    upserted_pull_request.close! # close the PR
+
+    if merge.ok?
+      Result.new(ok?: true)
+    else
+      return Result.new(ok?: false, error: "Failed!")
+    end
+  end
+end
+
+def create
+  # creates a PR and returns a Result
+end
+
+def merge
+  # merges a PR and returns a Result
+end
+
+Result = Struct.new(:ok?, :error, :value, keyword_init: true)
+
+

One problem here is that we're using custom Result objects which are not very chainable. But the other more shape-y problem is that we're having to check the output from merge, return an ok-Result or else cancel the transaction and then return a not-ok-Result. This not only feels like excessive work but also the use of return is unfortunate to essentially carry out a rollback + value type scenario.

+

With Github::Result we can rewrite it much more cleanly,

+
def create_and_merge!
+   return GitHub::Result.new { raise CreateError } unless create.ok?
+   upserted_pull_request =
+     @new_pull_request.update_or_insert!(create.value!)
+
+   GitHub::Result.new do
+     transaction do
+       upserted_pull_request.close! # close the PR
+       merge.value!
+     end
+  end
+end
+
+

As long as merge throws an exception, value! will raise it, rollback (throwing an exception will rollback) and opaquely pass it further up to the wrappper Result. This allows us to avoid return magic and ugly raises in the middle of the transaction block and chain the exceptions up.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2023-01-18t13-41-44/index.html b/post-2023-01-18t13-41-44/index.html new file mode 100644 index 0000000..a5be3c4 --- /dev/null +++ b/post-2023-01-18t13-41-44/index.html @@ -0,0 +1,122 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2023 / Jan 18 — 13:41
+
kitallis
+ + + + + + + + +
+
+
+
+
+

All these years of using Rails, I had no idea has_one did this.

+

+

More importantly with a relationship like follows,

+
class Model < ActiveRecord
+  has_one :build
+end
+
+class Build < ActiveRecord
+  has_one_attached_file :file
+end
+
+

If I create_build on Model if one already exists, it will:

+
    +
  1. Delete that Build, create a new one and attach it to Model
  2. +
  3. Kickoff an ActiveStorage::PurgeJob and delete the file from GCS/AWS
  4. +
+

This is highly uncomfortable.

+ +
+
+
+
+
+ + +
+ + diff --git a/post-2023-08-08t19-41-37/index.html b/post-2023-08-08t19-41-37/index.html new file mode 100644 index 0000000..918d0a4 --- /dev/null +++ b/post-2023-08-08t19-41-37/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + Tramline | Engineering notes + + + + + +
+ +
+ +
+
+
+
+

+ Short notes from engineering +

+
+

+ Micro-posts on programming and patterns as we build Tramline in public. + Follow this feed if you find these bits interesting. +

+
+
+
+ + +
+
+
+
+
+
+
2023 / Aug 08 — 19:41
+
kitallis
+ + + + + + + + +
+
+
+
+
+

then in Ruby feels like a little sister to the as-> macro from clojure.

+

Most of the API code (and several other parts) in Tramline heavily make use of then

+

example, example, example.

+

There's a couple of interesting things of note here.

+

One, I think numbered params have a lot of scope in removing some of the block cruft and overnaming of things across then blocks. Especially if the next in chain is visually obvious.

+
@client.workflows(repo)
+       .then { fetch_workflows(*1) }
+       .then { pick_active(*1) }
+       .then { transform_keys(*1) }
+
+

The above feels more natural and less noisy than naming each intermediate step with similar sounding variable names.

+

The second one is a controversial (perhaps even wrong) point. It seems to me that since then is just a function, the general debuggability of something going wrong in the pipeline is easier to find out in the chain.

+

My (now fading) experience with threading macro debuggability has been less efficient; I end up adding taps and spies to figure out what part of the chain broke.

+

I prefer an experience like:

+
    +
  1. write a pipeline
  2. +
  3. pipeline breaks in expression number 2
  4. +
  5. errors output nudges you clearly in the direction of just tweaking expression number 2
  6. +
  7. make the fix and the pipeline starts working again
  8. +
+ +
+
+
+
+
+ + +
+ + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..54074e5 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: +Allow: / +Sitemap: https://devnotes.tramline.app/sitemap.xml diff --git a/search_index.en.js b/search_index.en.js new file mode 100644 index 0000000..513d178 --- /dev/null +++ b/search_index.en.js @@ -0,0 +1 @@ +window.searchIndex = {"fields":["title","body"],"pipeline":["trimmer","stopWordFilter","stemmer"],"ref":"id","version":"0.9.5","index":{"body":{"root":{"docs":{},"df":0,"1":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2,".":{"docs":{},"df":0,"3":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"0":{"docs":{},"df":0,"0":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"9":{"docs":{},"df":0,"9":{"docs":{},"df":0,"9":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}}},"2":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":2,"0":{"docs":{},"df":0,"0":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"9":{"docs":{},"df":0,"3":{"docs":{},"df":0,"4":{"docs":{},"df":0,"7":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"3":{"docs":{},"df":0,".":{"docs":{},"df":0,"0":{"docs":{},"df":0,".":{"docs":{},"df":0,"y":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}},"1":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"4":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1},"7":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1},"8":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1,"3":{"docs":{},"df":0,".":{"docs":{},"df":0,"6":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"6":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"a":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"b":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}}}},"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":3}}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}}},"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":3,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"g":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951}},"df":1},"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":4}}}}}},"n":{"docs":{},"df":0,"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"y":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2},"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}}},"t":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}},"v":{"docs":{},"df":0,"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":3}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}},"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"g":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}}}}},"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}}},"i":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951}},"df":1},"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1},"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":5}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":2}}}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.7320508075688772}},"df":2}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}},"m":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}},"u":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.7320508075688772}},"df":2,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":2}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}},"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":3}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}},"o":{"docs":{},"df":0,"j":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772}},"df":1}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":5,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}}}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2},"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1},"x":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"/":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}},"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":2,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}}}},".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}},"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}}}}}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1}}}}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}}}}},"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"f":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":2}}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":2}}},"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}}}}}},"b":{"docs":{},"df":0,"/":{"docs":{},"df":0,"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,".":{"docs":{},"df":0,"r":{"docs":{},"df":0,"b":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.4142135623730951}},"df":1}},"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}}}},"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}},"f":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.0}},"df":2,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"y":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":1}}}},"i":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"v":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}}}}},"o":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"y":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":2.23606797749979}},"df":2}}},"c":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}}},"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.8284271247461903},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":4},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951}},"df":2}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}}},"u":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":2.0}},"df":1}}},"r":{"docs":{},"df":0,"b":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":2.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":3}}}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"y":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}}},"x":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.7320508075688772}},"df":2}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"u":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":2}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}}},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}}}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}},"d":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1},"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":3}},"l":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":4,"e":{"docs":{},"df":0,"y":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1},"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}}}},"e":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":2}},"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"_":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,"f":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"1":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}},"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"g":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":1},"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}}}},"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"x":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":3}}}},"r":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"g":{"docs":{},"df":0,"c":{"docs":{},"df":0,"s":{"docs":{},"df":0,"/":{"docs":{},"df":0,"a":{"docs":{},"df":0,"w":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,":":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":2,".":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}},"o":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2,"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}}}},"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1},"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":1,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":3,"'":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}}},"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"'":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2},"l":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":2}},"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":6},"v":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}},"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":2}}},"m":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}}}}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"_":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}},"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,".":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}},"n":{"docs":{},"df":0,"'":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"t":{"docs":{},"df":0,"'":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951}},"df":3}}},"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"b":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"t":{"docs":{"https://devnotes.tramline.app/":{"tf":1.0}},"df":1},"y":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"s":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1,"x":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}},"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":3}},"y":{"docs":{},"df":0,"w":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1,"o":{"docs":{},"df":0,"f":{"docs":{},"df":0,"f":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}},"n":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":2}}}},"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951}},"df":1}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}},"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":3}}}},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":2.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.4142135623730951}},"df":2}},"g":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":2.449489742783178}},"df":1,"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}},"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951}},"df":1}},"m":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}}},"d":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"k":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":2}},"n":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}},"r":{"docs":{},"df":0,"k":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}},"x":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1,"i":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.23606797749979}},"df":1,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"d":{"docs":{},"df":0,"d":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.7320508075688772}},"df":3}},"u":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":5}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":2}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"b":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}}},"e":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}},"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":3}},"w":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"_":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"_":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"!":{"docs":{},"df":0,"(":{"docs":{},"df":0,"c":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,".":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}},"x":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"t":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":5}},"u":{"docs":{},"df":0,"d":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"b":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.7320508075688772}},"df":1}}}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":4}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"d":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"f":{"docs":{},"df":0,"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":3},"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":4},"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,".":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}}}}}}}},"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"c":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":5,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951}},"df":1}}}}},"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1},"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}},"y":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.7320508075688772}},"df":1}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{},"df":0,"_":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"1":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}}}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":2.0}},"df":2}}}}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}},"y":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"o":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}},"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}},"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.0}},"df":1,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"f":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}},"t":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}},"o":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}},"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":2}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"l":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}},"i":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":2.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":5},"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772}},"df":1}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"b":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"z":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951}},"df":1}}},"s":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"f":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}},"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}}}}},"i":{"docs":{},"df":0,"e":{"docs":{},"df":0,"f":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"m":{"docs":{},"df":0,"b":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"v":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}}}},"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"i":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"_":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"_":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.6457513110645907}},"df":2,".":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772}},"df":2}}}}}}}}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"r":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":3.4641016151377544}},"df":1}}}},"w":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"b":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.0}},"df":1}}}}}},"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.4142135623730951}},"df":2}},"s":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":2.0}},"df":1,"/":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"t":{"docs":{},"df":0,"i":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"e":{"docs":{},"df":0,"x":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}}}},"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}}}}}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2},"o":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":2.0}},"df":1}}}}},"n":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}},"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"o":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}}},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"e":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":3,"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}}},"r":{"docs":{},"df":0,"v":{"docs":{},"df":0,"i":{"docs":{},"df":0,"c":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":2.0}},"df":1}}}},"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}},"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"c":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"n":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"i":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}}}},"s":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}},"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"w":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}},"m":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"p":{"docs":{},"df":0,"a":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"i":{"docs":{},"df":0,"f":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":2.0}},"df":1}}}}},"r":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}},"t":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":3},"u":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951}},"df":1}}},"e":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0}},"df":1}},"l":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}},"m":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}},"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.7320508075688772}},"df":1}}},"i":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}},"u":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{},"df":0,".":{"docs":{},"df":0,"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"w":{"docs":{},"df":0,"(":{"docs":{},"df":0,":":{"docs":{},"df":0,"o":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":2}}}}}}}}}}}},"u":{"docs":{},"df":0,"f":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"j":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1,"(":{"docs":{},"df":0,":":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}},"g":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":2},"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}}}},"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.4142135623730951}},"df":1}}}}}},"t":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"g":{"docs":{},"df":0,"e":{"docs":{},"df":0,"t":{"docs":{},"df":0,"r":{"docs":{},"df":0,"u":{"docs":{},"df":0,"b":{"docs":{},"df":0,"y":{"docs":{},"df":0,"v":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}}}}}}}}}}},"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"i":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}},"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"l":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}},"r":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-36-17/":{"tf":1.0}},"df":2}}}}},"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}},"h":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"'":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":3},"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":4}}},"o":{"docs":{},"df":0,"s":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"u":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}}},"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}},"w":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}},"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"o":{"docs":{},"df":0,"u":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772}},"df":1}}}},"i":{"docs":{},"df":0,"g":{"docs":{},"df":0,"h":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}},"m":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}},"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"m":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"n":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.8284271247461903}},"df":3}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"m":{"docs":{},"df":0,"_":{"docs":{},"df":0,"k":{"docs":{},"df":0,"e":{"docs":{},"df":0,"y":{"docs":{},"df":0,"s":{"docs":{},"df":0,"(":{"docs":{},"df":0,"*":{"docs":{},"df":0,"1":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}}}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.7320508075688772}},"df":1}}}}},"u":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":4}}},"w":{"docs":{},"df":0,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}},"y":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}},"u":{"docs":{},"df":0,"g":{"docs":{},"df":0,"l":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}},"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1},"n":{"docs":{},"df":0,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}}},"f":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{},"df":0,"u":{"docs":{},"df":0,"i":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0}},"df":1}}}}}},"l":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}},"n":{"docs":{},"df":0,"e":{"docs":{},"df":0,"c":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"s":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":2}}}}}}}}},"p":{"docs":{},"df":0,"a":{"docs":{},"df":0,"c":{"docs":{},"df":0,"k":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":1}}}}},"p":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":5,"f":{"docs":{},"df":0,"r":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"s":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"d":{"docs":{},"df":0,"_":{"docs":{},"df":0,"p":{"docs":{},"df":0,"u":{"docs":{},"df":0,"l":{"docs":{},"df":0,"l":{"docs":{},"df":0,"_":{"docs":{},"df":0,"r":{"docs":{},"df":0,"e":{"docs":{},"df":0,"q":{"docs":{},"df":0,"u":{"docs":{},"df":0,"e":{"docs":{},"df":0,"s":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1,".":{"docs":{},"df":0,"c":{"docs":{},"df":0,"l":{"docs":{},"df":0,"o":{"docs":{},"df":0,"s":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}}}}}}}}}}}}}}}}}}}}}}}}},"s":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":2.23606797749979},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":6}},"v":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.7320508075688772},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.7320508075688772}},"df":2}},"r":{"docs":{},"df":0,"i":{"docs":{},"df":0,"a":{"docs":{},"df":0,"b":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}},"o":{"docs":{},"df":0,"u":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0}},"df":2}}}}},"e":{"docs":{},"df":0,"n":{"docs":{},"df":0,"d":{"docs":{},"df":0,"o":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1}}}},"r":{"docs":{},"df":0,"i":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":4}}},"i":{"docs":{},"df":0,"a":{"docs":{"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":1},"e":{"docs":{},"df":0,"w":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.7320508075688772}},"df":2,"c":{"docs":{},"df":0,"o":{"docs":{},"df":0,"m":{"docs":{},"df":0,"p":{"docs":{},"df":0,"o":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}}}}}}},"s":{"docs":{},"df":0,"u":{"docs":{},"df":0,"a":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":1}}}}}},"w":{"docs":{},"df":0,"a":{"docs":{},"df":0,"n":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"tf":1.0}},"df":1}},"y":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"tf":1.0}},"df":2}},"e":{"docs":{},"df":0,"'":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.4142135623730951}},"df":1}},"b":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1},"l":{"docs":{},"df":0,"l":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":2}}},"h":{"docs":{},"df":0,"a":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{},"df":0,"v":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"h":{"docs":{},"df":0,"i":{"docs":{},"df":0,"n":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0}},"df":1}}}}},"o":{"docs":{},"df":0,"r":{"docs":{},"df":0,"d":{"docs":{"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.0}},"df":1},"k":{"docs":{"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"tf":1.0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"tf":1.4142135623730951},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":5,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"tf":1.0}},"df":1}}}}},"r":{"docs":{},"df":0,"a":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"p":{"docs":{},"df":0,"e":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1}}}}}},"i":{"docs":{},"df":0,"t":{"docs":{},"df":0,"e":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.0}},"df":2}}},"o":{"docs":{},"df":0,"n":{"docs":{},"df":0,"g":{"docs":{"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"tf":1.4142135623730951}},"df":1}}}}},"y":{"docs":{"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"tf":1.0}},"df":1,"e":{"docs":{},"df":0,"a":{"docs":{},"df":0,"r":{"docs":{"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"tf":1.0}},"df":1}}}}}},"title":{"root":{"docs":{},"df":0,"j":{"docs":{},"df":0,"o":{"docs":{},"df":0,"t":{"docs":{"https://devnotes.tramline.app/":{"tf":1.0}},"df":1}}}}}},"documentStore":{"save":true,"docs":{"https://devnotes.tramline.app/":{"body":"","id":"https://devnotes.tramline.app/","title":"Jots"},"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"body":"The log streams feature in Render is a little bit strange. From the docs,\nRender Log Streams forward logs from your web services,\nprivate services, background workers, databases,\nand cron jobs to any logging provider that supplies a TLS-enabled syslog drain.\n\nI hooked this up with datadog and it is indeed a syslog drain across all services. This means my newly setup datadog is now filled with thousands of non-application related logs. Not only now do I have to filter all these out, but also pay for them.\nSurely a log streaming pipeline should have a minimum of a service-level (if not content-level) filtering as baseline features?\n","id":"https://devnotes.tramline.app/post-2022-09-25t21-18-31/","title":""},"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"body":"I'm finding keeping POROs seperated away from models/ a mental relief for some reason. I think I'm going to stick to it.\n\n","id":"https://devnotes.tramline.app/post-2022-09-26t11-37-01/","title":""},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"body":"It's not terribly hard to come up with a state-machine DSL like aasm\nby hand, but the ActiveRecord support is very handy.\nI'm using it with an enum against a status field with the following options:\n{\n column: :status,\n requires_lock: true,\n requires_new_transaction: false,\n enum: true,\n create_scopes: false\n}\n\nThe requires_new_transaction is false to avoid landing into unnecessary nested transactions. I'll keep my transactions explicit and intentional.\nThe enum allows me to play well with the Rails enum which already generates scopes, so I keep create_scopes off.\nThe requires_lock is seemingly quite handy at first glance, since I require locking rows a fair bit because of the distributed nature of the app.\nBut the level at which it locks is far too granular. I generally seem to require locking rows for far longer and more work than simply transitioning state and not everything can be sanely encapsulated in a before or after.\nI'd rather provide the library with methods that cause a state transition than the inverted way it currently operates: generating methods to transition for you and then locking within that event.\n","id":"https://devnotes.tramline.app/post-2022-09-28t09-02-54/","title":""},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"body":"The views currently are a hot mess, giant nested ERB templates.\nI'm not convinced of jumping into ViewComponents yet, since the shape of this UI is so nebulous and will definitely change dramatically and I do not want to deal with more objects and more abstractions and more code at the moment.\nI'll deal with the complex markup like it's 1999.\n","id":"https://devnotes.tramline.app/post-2022-09-28t11-15-39/","title":""},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"body":"There are a few tight, but rare-enough race-conditions lingering about and they are constantly bothering me. They just exist as FIXMEs in the code atm.\nMost of the these seem to be solvable by pessimistic row-locks, but I'm already dreading the littering of necessary but unintuitive placements of various row-locks throughout the code-base.\nWould modelling this as a pure declarative orchestrator help keep this in check?\nNot sure.\n","id":"https://devnotes.tramline.app/post-2022-09-28t11-15-40/","title":""},"https://devnotes.tramline.app/post-2022-10-05t00-19-39/":{"body":"\n","id":"https://devnotes.tramline.app/post-2022-10-05t00-19-39/","title":""},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"body":"When I originally kicked things off, I added a lot of big transactions around various api-call-surrounding-database-ops. This was knowingly a cheap hack to avoid bad state changes and delaying the inevitable: careful and tedious handling of errors and control-flow.\nI'm now slowly refactoring things to unpack these big transactions and I realize many of them are actually unnecessary. I'm employing yet another cheap Result object to signify boundary-states,\nResult = Struct.new(:ok?, :error, :value, keyword_init: true)\n\ndef good_op\n Result.new(ok?: true, value: 1)\nend\n\ndef bad_op\n Result.new(ok?: false, error: \"Did not work\")\nend\n\noperation.ok?\noperation.value\noperation.error\n\nThis is a little bit silly, because it requires you to ensure that value is only present alongside the ok? and the others are falsey in the error case. But it is very simple and quick to get a refactor busted out.\nI have used Github::Result in the past, but recently discovered dry-rb -- many of these look very sensible and immediately useful. Hopefully the next step!\n","id":"https://devnotes.tramline.app/post-2022-10-06t12-23-54/","title":""},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"body":"Here's a spectrum of componentizing views I've been thinking about lately (in Rails):\n\nExtract logical helpers and partials\n\nThey are often like stuffing away code inside a Module and calling it a refactor. But it is cheap and effective enough to start off with. You often run into \"too many allocations\" issues, with nested views and partials. They also don't abstract state very well. Arguments for their limitations have been made ad naseam - ex.\nCompleted 200 OK in 86ms (Views: 83.6ms | ActiveRecord: 1.3ms | Allocations: 29347)\n\n\nPresenter objects / ViewComponents\n\nSolid ideas here. Fair bit of upfront work required and you have to be meticulous about the granularity of your components. These claim to be very fast by precompiling templates at rails boot.\n\nSPAs, building JSX components (or whatever else is out there now)\n\nI'm not convinced I'd need to do this. I think Stimulus / Hotwired is powerful enough for all our needs for the forseeable future (famous last words). I still remember the joy of ripping out all the react from a previous codebase I worked on and rewrote them in a much simpler, basic markup and js.\n#1 ➡ #2 hopefuly in a few months time.\n","id":"https://devnotes.tramline.app/post-2022-10-11t15-44-45/","title":""},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"body":"I decided to add stricter linting to the codebase. Since I like what standard offers, I simply extended it with rails specific lints, in the following way:\nrequire:\n - standard\n - rubocop-rails\n - rubocop-rspec\n - rubocop-performance\n\ninherit_gem:\n standard: config/ruby-3.0.yml\n\nAllCops:\n TargetRubyVersion: 3.1\n NewCops: enable\n Exclude:\n - bin/**/*\n - public/**/*\n - vendor/**/*\n - db/schema.rb\n\nRails:\n Enabled: true\n\nRSpec:\n Enabled: true\n\nRSpec/ExampleLength:\n Enabled: false\n\nRSpec/MultipleExpectations:\n Max: 4\n\nPerformance:\n Enabled: true\n\nThis ensures standard is used for ruby things, and rails, rspec and some other performance related checks in addition to it via rubocop.\nI came across this suggestion while fixing the 100 odd offenses it threw up,\nRSpec/NamedSubject:\n Name your test subject if you need to reference it explicitly.\n\nThe fix,\nsubject(:run) { create(:releases_train_run) }\n\nBut what even is this? Isn't this just a let really? You can't really use the is_expected.to shortcut either since it's now named. I cannot possibly make the claim that this reads better.\nI think it's just one of those annoying rspec maximalisms.\n","id":"https://devnotes.tramline.app/post-2023-01-06t14-25-59/","title":""},"https://devnotes.tramline.app/post-2023-01-06t14-36-17/":{"body":"\nThis is terrible.\n","id":"https://devnotes.tramline.app/post-2023-01-06t14-36-17/","title":""},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"body":"I have been using return a fair bit in transactions in Rails. In 7, these rollback the transaction and I use them as so.\nThey obviously require very careful writing and to remember that the return actually just doesn't jump out of the block, it cancels the transaction.\nI prefer the fact that return from 8 will throw an exception, but I've found that using Github::Result around transactions as a pattern comes pretty close or is better (in some cases) already.\nConsider this example code that creates and merges a pull request,\ndef create_and_merge!\n return Result.new(ok?: false) unless create.ok?\n upserted_pull_request =\n @new_pull_request.update_or_insert!(create.value)\n\n transaction do\n upserted_pull_request.close! # close the PR\n\n if merge.ok?\n Result.new(ok?: true)\n else\n return Result.new(ok?: false, error: \"Failed!\")\n end\n end\nend\n\ndef create\n # creates a PR and returns a Result\nend\n\ndef merge\n # merges a PR and returns a Result\nend\n\nResult = Struct.new(:ok?, :error, :value, keyword_init: true)\n\nOne problem here is that we're using custom Result objects which are not very chainable. But the other more shape-y problem is that we're having to check the output from merge, return an ok-Result or else cancel the transaction and then return a not-ok-Result. This not only feels like excessive work but also the use of return is unfortunate to essentially carry out a rollback + value type scenario.\nWith Github::Result we can rewrite it much more cleanly,\ndef create_and_merge!\n return GitHub::Result.new { raise CreateError } unless create.ok?\n upserted_pull_request =\n @new_pull_request.update_or_insert!(create.value!)\n\n GitHub::Result.new do\n transaction do\n upserted_pull_request.close! # close the PR\n merge.value!\n end\n end\nend\n\nAs long as merge throws an exception, value! will raise it, rollback (throwing an exception will rollback) and opaquely pass it further up to the wrappper Result. This allows us to avoid return magic and ugly raises in the middle of the transaction block and chain the exceptions up.\n","id":"https://devnotes.tramline.app/post-2023-01-10t14-58-17/","title":""},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"body":"All these years of using Rails, I had no idea has_one did this.\n\nMore importantly with a relationship like follows,\nclass Model < ActiveRecord\n has_one :build\nend\n\nclass Build < ActiveRecord\n has_one_attached_file :file\nend\n\nIf I create_build on Model if one already exists, it will:\n\nDelete that Build, create a new one and attach it to Model\nKickoff an ActiveStorage::PurgeJob and delete the file from GCS/AWS\n\nThis is highly uncomfortable.\n","id":"https://devnotes.tramline.app/post-2023-01-18t13-41-44/","title":""},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"body":"then in Ruby feels like a little sister to the as-> macro from clojure.\nMost of the API code (and several other parts) in Tramline heavily make use of then\nexample, example, example.\nThere's a couple of interesting things of note here.\nOne, I think numbered params have a lot of scope in removing some of the block cruft and overnaming of things across then blocks. Especially if the next in chain is visually obvious.\n@client.workflows(repo)\n .then { fetch_workflows(*1) }\n .then { pick_active(*1) }\n .then { transform_keys(*1) }\n\nThe above feels more natural and less noisy than naming each intermediate step with similar sounding variable names.\nThe second one is a controversial (perhaps even wrong) point. It seems to me that since then is just a function, the general debuggability of something going wrong in the pipeline is easier to find out in the chain.\nMy (now fading) experience with threading macro debuggability has been less efficient; I end up adding taps and spies to figure out what part of the chain broke.\nI prefer an experience like:\n\nwrite a pipeline\npipeline breaks in expression number 2\nerrors output nudges you clearly in the direction of just tweaking expression number 2\nmake the fix and the pipeline starts working again\n\n","id":"https://devnotes.tramline.app/post-2023-08-08t19-41-37/","title":""}},"docInfo":{"https://devnotes.tramline.app/":{"body":0,"title":1},"https://devnotes.tramline.app/post-2022-09-25t21-18-31/":{"body":63,"title":0},"https://devnotes.tramline.app/post-2022-09-26t11-37-01/":{"body":14,"title":0},"https://devnotes.tramline.app/post-2022-09-28t09-02-54/":{"body":106,"title":0},"https://devnotes.tramline.app/post-2022-09-28t11-15-39/":{"body":33,"title":0},"https://devnotes.tramline.app/post-2022-09-28t11-15-40/":{"body":39,"title":0},"https://devnotes.tramline.app/post-2022-10-05t00-19-39/":{"body":0,"title":0},"https://devnotes.tramline.app/post-2022-10-06t12-23-54/":{"body":108,"title":0},"https://devnotes.tramline.app/post-2022-10-11t15-44-45/":{"body":115,"title":0},"https://devnotes.tramline.app/post-2023-01-06t14-25-59/":{"body":105,"title":0},"https://devnotes.tramline.app/post-2023-01-06t14-36-17/":{"body":1,"title":0},"https://devnotes.tramline.app/post-2023-01-10t14-58-17/":{"body":186,"title":0},"https://devnotes.tramline.app/post-2023-01-18t13-41-44/":{"body":42,"title":0},"https://devnotes.tramline.app/post-2023-08-08t19-41-37/":{"body":120,"title":0}},"length":14},"lang":"English"}; \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..3ac8abe --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,61 @@ + + + + https://devnotes.tramline.app/ + + + https://devnotes.tramline.app/page/1/ + + + https://devnotes.tramline.app/post-2022-09-25t21-18-31/ + 2022-09-25T21:18:31 + + + https://devnotes.tramline.app/post-2022-09-26t11-37-01/ + 2022-09-26T11:37:01 + + + https://devnotes.tramline.app/post-2022-09-28t09-02-54/ + 2022-09-28T09:02:54 + + + https://devnotes.tramline.app/post-2022-09-28t11-15-39/ + 2022-09-28T11:15:39 + + + https://devnotes.tramline.app/post-2022-09-28t11-15-40/ + 2022-09-28T11:15:40 + + + https://devnotes.tramline.app/post-2022-10-05t00-19-39/ + 2022-10-05T00:19:39 + + + https://devnotes.tramline.app/post-2022-10-06t12-23-54/ + 2022-10-06T12:23:54 + + + https://devnotes.tramline.app/post-2022-10-11t15-44-45/ + 2022-10-11T15:44:45 + + + https://devnotes.tramline.app/post-2023-01-06t14-25-59/ + 2023-01-06T14:25:59 + + + https://devnotes.tramline.app/post-2023-01-06t14-36-17/ + 2023-01-06T14:36:17 + + + https://devnotes.tramline.app/post-2023-01-10t14-58-17/ + 2023-01-10T14:58:17 + + + https://devnotes.tramline.app/post-2023-01-18t13-41-44/ + 2023-01-18T13:41:44 + + + https://devnotes.tramline.app/post-2023-08-08t19-41-37/ + 2023-08-08T19:41:37 + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..f90492b --- /dev/null +++ b/style.css @@ -0,0 +1 @@ +html,body{margin:0}body{--primary-bg: #fdfeff;--primary-text: #111111;--secondary-bg: #eeeef3;--secondary-text: #9b9b9b;--hover-bg: #dde1e5;--active-bg: #cdcfd2;--dark-primary-bg: #141516;--dark-primary-text: #ebebeb;--dark-secondary-bg: #30373a;--dark-secondary-text: #a4a7a9;--dark-hover-bg: #474c50;--dark-active-bg: #626569}.dark{--primary-bg: var(--dark-primary-bg);--primary-text: var(--dark-primary-text);--secondary-bg: var(--dark-secondary-bg);--secondary-text: var(--dark-secondary-text);--hover-bg: var(--dark-hover-bg);--active-bg: var(--dark-active-bg)}@media (prefers-color-scheme: dark){body:not(.light){--primary-bg: var(--dark-primary-bg);--primary-text: var(--dark-primary-text);--secondary-bg: var(--dark-secondary-bg);--secondary-text: var(--dark-secondary-text);--hover-bg: var(--dark-hover-bg);--active-bg: var(--dark-active-bg)}}body{font-family:"Bitter",system-ui,sans-serif;color:var(--primary-text);background:var(--primary-bg);display:flex;flex-direction:column;min-height:100vh;border-bottom:8px solid #111}input,button,textarea{font-size:1em;padding:.5em .8em;color:var(--primary-text);font-family:system-ui,sans-serif;tab-size:4}input::placeholder,textarea::placeholder{color:var(--secondary-text)}header,h1,main{width:calc(100% - 32px);max-width:860px;margin:1em auto}header{margin-bottom:2em;display:flex;flex-direction:row;align-items:center;justify-content:space-between}header .logo{font-weight:bold}nav{display:flex;flex-direction:row-reverse;align-items:center;gap:1em}header a,header button{display:inline;cursor:pointer;color:var(--primary-text);background:rgba(0,0,0,0);border:0;border-radius:0;text-decoration:none;padding:.5em 0}header a:hover,header button:hover{text-decoration:underline}h1{margin-top:.75em;margin-bottom:.25em;line-height:1.4em}main{margin-bottom:3em}.border-b{border-bottom:1px solid var(--secondary-text)}.mb-3{margin-bottom:3em}#search{position:relative;margin-bottom:2em;overflow:hidden}.pageControls{float:right;display:flex;flex-direction:row;align-items:center;justify-content:flex-end;gap:.75em}.pageControls a{color:var(--primary-text);text-decoration:none}.pageControls a:hover{text-decoration:underline}a.pageButton{display:flex;align-items:center;justify-content:center;font-size:1.5em;height:1.5em;width:1.5em;border-radius:50%;background:var(--secondary-bg)}a.pageButton:hover{background:var(--hover-bg);text-decoration:none}.message{font-style:italic;color:var(--secondary-text);margin-bottom:2em}.update{margin-bottom:2.5em;word-break:break-word}.update .update-t{font-size:14px;color:var(--secondary-text);margin-bottom:-.5em}.update .update-t a{color:var(--secondary-text);text-decoration:none}.update .update-t a:hover{text-decoration:underline}.update-t .relativestamp{margin-bottom:6px}.update h1,.update h2,.update h3{margin:.75em 0 .5em 0;line-height:1.4em}.update h1{font-size:1.75em}.update h2{font-size:1.5em}.update h3{font-size:1.2em}.update h4,.update h5,.update h6{font-size:1em}.update p,.update li{line-height:1.5em;max-width:64ch}.update strike{color:var(--secondary-text)}.update img{max-width:100%;max-height:500px;border-radius:6px}.update a{color:var(--primary-text);text-decoration:underline}.update ul,.update ol{padding-left:3ch}.update pre,.update code{font-size:1em;font-family:"IBM Plex Mono","Menlo","Monaco",monospace;word-break:initial}.update pre{border-radius:6px;box-sizing:border-box;padding:12px 8px;overflow-x:auto}.update code{padding:2px 10px;border-radius:6px}.update pre code{padding:0}.update blockquote{margin:0;border-left:4px solid var(--active-bg);padding-left:1em;display:block}@media only screen and (min-width: 760px){.update{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;margin-bottom:1.5em}.update-t{flex-grow:0;flex-shrink:0;width:134px;margin-top:3px}.update-s{width:0;flex-grow:1;flex-shrink:1}.update-s :first-child{margin-top:0}} \ No newline at end of file diff --git a/styles/header.css b/styles/header.css new file mode 100644 index 0000000..baa6b38 --- /dev/null +++ b/styles/header.css @@ -0,0 +1,477 @@ +// This is extracted CSS from tramline.app to render the tramline.app header +// It is probably not very good CSS. + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/e91fed/00000000000000007735fd7a/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i4&v=3) format("woff2"),url(https://use.typekit.net/af/e91fed/00000000000000007735fd7a/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i4&v=3) format("woff"),url(https://use.typekit.net/af/e91fed/00000000000000007735fd7a/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i4&v=3) format("opentype"); + font-weight:400; + font-style:italic; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/fdbde9/00000000000000007735fd85/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3) format("woff2"),url(https://use.typekit.net/af/fdbde9/00000000000000007735fd85/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3) format("woff"),url(https://use.typekit.net/af/fdbde9/00000000000000007735fd85/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n4&v=3) format("opentype"); + font-weight:400; + font-style:normal; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/081868/00000000000000007735fd7d/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i5&v=3) format("woff2"),url(https://use.typekit.net/af/081868/00000000000000007735fd7d/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i5&v=3) format("woff"),url(https://use.typekit.net/af/081868/00000000000000007735fd7d/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i5&v=3) format("opentype"); + font-weight:500; + font-style:italic; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/978d4f/00000000000000007735fd81/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("woff2"),url(https://use.typekit.net/af/978d4f/00000000000000007735fd81/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("woff"),url(https://use.typekit.net/af/978d4f/00000000000000007735fd81/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("opentype"); + font-weight:500; + font-style:normal; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/7bfe96/00000000000000007735fd7f/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3) format("woff2"),url(https://use.typekit.net/af/7bfe96/00000000000000007735fd7f/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3) format("woff"),url(https://use.typekit.net/af/7bfe96/00000000000000007735fd7f/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n7&v=3) format("opentype"); + font-weight:700; + font-style:normal; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:objektiv-mk1; + src:url(https://use.typekit.net/af/4e7a51/00000000000000007735fd7e/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i7&v=3) format("woff2"),url(https://use.typekit.net/af/4e7a51/00000000000000007735fd7e/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i7&v=3) format("woff"),url(https://use.typekit.net/af/4e7a51/00000000000000007735fd7e/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=i7&v=3) format("opentype"); + font-weight:700; + font-style:italic; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:new-spirit; + src:url(https://use.typekit.net/af/de4bea/00000000000000007735c4bc/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("woff2"),url(https://use.typekit.net/af/de4bea/00000000000000007735c4bc/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("woff"),url(https://use.typekit.net/af/de4bea/00000000000000007735c4bc/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n5&v=3) format("opentype"); + font-weight:500; + font-style:normal; + font-stretch:normal; + font-display:auto; +} + +@font-face { + font-family:new-spirit; + src:url(https://use.typekit.net/af/19f9aa/00000000000000007735c4be/30/l?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n6&v=3) format("woff2"),url(https://use.typekit.net/af/19f9aa/00000000000000007735c4be/30/d?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n6&v=3) format("woff"),url(https://use.typekit.net/af/19f9aa/00000000000000007735c4be/30/a?primer=7cdcb44be4a7db8877ffa5c0007b8dd865b3bbc383831fe2ea177f62257a9191&fvd=n6&v=3) format("opentype"); + font-weight:600; + font-style:normal; + font-stretch:normal; + font-display:auto; +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100%; + background-color: #fff; + font-family: objektiv-mk1,sans-serif; + color: #2c323a; + font-size: 18px; + line-height: 1.6em; + font-weight: 400; +} + +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + height: 100%; +} + +.w-nav { + position: relative; + background: #ddd; + z-index: 1000; +} + +.nav-bar { + position: -webkit-sticky; + position: sticky; + left: 0; + top: 0; + right: 0; + z-index: 200; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + background-color: #fff; + box-shadow: 0 0 3px 0 #c3cacf; + font-family: objektiv-mk3; +} + +.w-nav:after,.w-nav:before { + content: " "; + display: table; + grid-column-start: 1; + grid-row-start: 1; + grid-column-end: 2; + grid-row-end: 2; +} + +.w-nav:after { + clear: both; +} + +.w-container { + margin-left: auto; + margin-right: auto; + max-width: 940px; +} + +.nav-container { + left: 0; + top: 0; + right: 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + width: 100%; + max-width: 1230px; + margin-right: auto; + margin-left: auto; + padding: 12px 50px; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.w-container:after,.w-container:before { + content: " "; + display: table; + grid-column-start: 1; + grid-row-start: 1; + grid-column-end: 2; + grid-row-end: 2; +} + +.w-container:after { + clear: both; +} + +.w-nav-overlay { + position: absolute; + overflow: hidden; + display: none; + top: 100%; + left: 0; + right: 0; + width: 100%; +} + +.logo-div { + display: block; + padding-right: 15px; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 0; + -webkit-flex: 0 auto; + -ms-flex: 0 auto; + flex: 0 auto; +} + +nav { + display: block; +} + +.w-nav-menu { + position: relative; + float: right; +} + +.nav-content { + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + margin-right: auto; + margin-left: auto; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.w-nav-button { + position: relative; + float: right; + padding: 18px; + font-size: 24px; + display: none; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + tap-highlight-color: rgba(0,0,0,0); + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +a { + background-color: transparent; + -webkit-transition: color .2s; + transition: color .2s; + color: #1a9c1d; + text-decoration: none; +} + +.w-inline-block { + max-width: 100%; + display: inline-block; +} + +.nav-logo { + -webkit-transition: opacity .2s; + transition: opacity .2s; +} + +a:active,a:hover { + outline: 0; +} + +a:hover { + color: #1a9c1d; +} + +.nav-logo:hover { + opacity: .75; +} + +.nav-menu { + -webkit-box-flex: 0; + -webkit-flex: 0 auto; + -ms-flex: 0 auto; + flex: 0 auto; + text-align: center; +} + +.nav-cta-button-container { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; +} + +img { + border: 0; + vertical-align: middle; + display: inline-block; + max-width: 100%; +} + +.logo:hover { + opacity: 1; +} + +.w-nav-link { + position: relative; + display: inline-block; + vertical-align: top; + text-decoration: none; + color: #222; + padding: 20px; + text-align: left; + margin-left: auto; + margin-right: auto; +} + +.nav-link { + padding: 5px 11px; + border-radius: 5px; + -webkit-transition: color .2s,-webkit-transform .2s; + transition: transform .2s,color .2s,-webkit-transform .2s; + font-family: objektiv-mk1,sans-serif; + color: #2c323a; + font-size: 15px; + line-height: 24px; + font-weight: 400; + text-transform: none; + cursor: pointer; +} + +.w-nav-link.w--current { + color: #0082f3; +} + +.nav-link.w--current { + font-family: objektiv-mk1,sans-serif; + color: #2c323a; + font-weight: 700; + text-transform: none; +} + +.nav-link.tramline { + font-family: objektiv-mk1,sans-serif; + font-size: 15px; + font-weight: 700; + letter-spacing: 1px; + text-transform: uppercase; +} + +.nav-link:hover { + -webkit-transform: translate(0,-1px); + -ms-transform: translate(0,-1px); + transform: translate(0,-1px); + color: #2c323a; +} + +.nav-link.w--current:hover { + -webkit-transform: translate(0,-1px); + -ms-transform: translate(0,-1px); + transform: translate(0,-1px); +} + +.nav-link.minimal { + margin-right: 8px; + color: #1a9c1d; + font-size: 14px; + font-weight: 700; + letter-spacing: 1px; + text-transform: uppercase; +} + +.nav-link.cta { + padding-right: 15px; + padding-left: 15px; + border-radius: 6px; + background-color: #3ac63f; + box-shadow: 0 1px 38px 0 rgba(0,0,0,.03); + color: #fff; + font-size: 14px; + font-weight: 700; + text-align: center; + letter-spacing: 1px; + text-transform: uppercase; +} + +.nav-link.cta:hover { + background-color: #1a9c1d; +} + + +/* These were inline style tags. Uses id+class to override almost everything */ +#style-mCOZw.style-mCOZw { + max-width: 1230px; +} +#style-233iE.style-233iE { + max-width: 1230px; +} +#style-Bh6WI.style-Bh6WI { + max-width: 1230px; +} +#style-gFOzb.style-gFOzb { + max-width: 1230px; +} +#style-TAxeQ.style-TAxeQ { + max-width: 1230px; +} +#style-pF8kx.style-pF8kx { + max-width: 1230px; +} +#style-hcA5o.style-hcA5o { + max-width: 1230px; +} +#style-4pCWX.style-4pCWX { + -webkit-user-select: text; +} +#style-J5dMq.style-J5dMq { + opacity: 1; + transform: translate3d(0px, 0px, 0px) scale3d(1, 1, 1) rotateX(0deg) rotateY(0deg) rotateZ(0deg) skew(0deg, 0deg); + transform-style: preserve-3d; +} + +h1 { + margin: 0 0 24px; + padding-top: 10px; + font-family: new-spirit,sans-serif; + color: #2c323a; + font-size: 44px; + line-height: 1em; + font-weight: 500; +} + +.heading-large { + max-width: 800px; + margin-right: auto; + margin-left: auto; + line-height: 1.1em; + font-weight: 600; +} + +.underline { + display: inline-block; + border-bottom: 8px #c4f34d; + opacity: 1; + text-decoration: underline; +} diff --git a/styles/styles.css b/styles/styles.css new file mode 100644 index 0000000..b351a56 --- /dev/null +++ b/styles/styles.css @@ -0,0 +1,1481 @@ +/* +! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +*/ + +html { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-size: 1em; + /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.prose { + color: var(--tw-prose-body); + max-width: 65ch; +} + +.prose :where(p):not(:where([class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + +.prose :where([class~="lead"]):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} + +.prose :where(a):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-links); + text-decoration: underline; + font-weight: 500; +} + +.prose :where(strong):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} + +.prose :where(a strong):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote strong):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th strong):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(ol):not(:where([class~="not-prose"] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} + +.prose :where(ol[type="A"]):not(:where([class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a"]):not(:where([class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="A" s]):not(:where([class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a" s]):not(:where([class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="I"]):not(:where([class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i"]):not(:where([class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="I" s]):not(:where([class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i" s]):not(:where([class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="1"]):not(:where([class~="not-prose"] *)) { + list-style-type: decimal; +} + +.prose :where(ul):not(:where([class~="not-prose"] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-left: 1.625em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} + +.prose :where(ul > li):not(:where([class~="not-prose"] *))::marker { + color: var(--tw-prose-bullets); +} + +.prose :where(hr):not(:where([class~="not-prose"] *)) { + border-color: var(--tw-prose-hr); + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} + +.prose :where(blockquote):not(:where([class~="not-prose"] *)) { + font-weight: 500; + font-style: italic; + color: var(--tw-prose-quotes); + border-left-width: 0.25rem; + border-left-color: var(--tw-prose-quote-borders); + quotes: "\201C""\201D""\2018""\2019"; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-left: 1em; +} + +.prose :where(blockquote p:first-of-type):not(:where([class~="not-prose"] *))::before { + content: open-quote; +} + +.prose :where(blockquote p:last-of-type):not(:where([class~="not-prose"] *))::after { + content: close-quote; +} + +.prose :where(h1):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} + +.prose :where(h1 strong):not(:where([class~="not-prose"] *)) { + font-weight: 900; + color: inherit; +} + +.prose :where(h2):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} + +.prose :where(h2 strong):not(:where([class~="not-prose"] *)) { + font-weight: 800; + color: inherit; +} + +.prose :where(h3):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} + +.prose :where(h3 strong):not(:where([class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(h4):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} + +.prose :where(h4 strong):not(:where([class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(img):not(:where([class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(figure > *):not(:where([class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose :where(figcaption):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} + +.prose :where(code):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-code); + font-weight: 600; + font-size: 0.875em; +} + +.prose :where(code):not(:where([class~="not-prose"] *))::before { + content: "`"; +} + +.prose :where(code):not(:where([class~="not-prose"] *))::after { + content: "`"; +} + +.prose :where(a code):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h1 code):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h2 code):not(:where([class~="not-prose"] *)) { + color: inherit; + font-size: 0.875em; +} + +.prose :where(h3 code):not(:where([class~="not-prose"] *)) { + color: inherit; + font-size: 0.9em; +} + +.prose :where(h4 code):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote code):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th code):not(:where([class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(pre):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-pre-code); + background-color: var(--tw-prose-pre-bg); + overflow-x: auto; + font-weight: 400; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-right: 1.1428571em; + padding-bottom: 0.8571429em; + padding-left: 1.1428571em; +} + +.prose :where(pre code):not(:where([class~="not-prose"] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} + +.prose :where(pre code):not(:where([class~="not-prose"] *))::before { + content: none; +} + +.prose :where(pre code):not(:where([class~="not-prose"] *))::after { + content: none; +} + +.prose :where(table):not(:where([class~="not-prose"] *)) { + width: 100%; + table-layout: auto; + text-align: left; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} + +.prose :where(thead):not(:where([class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} + +.prose :where(thead th):not(:where([class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + +.prose :where(tbody tr):not(:where([class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} + +.prose :where(tbody tr:last-child):not(:where([class~="not-prose"] *)) { + border-bottom-width: 0; +} + +.prose :where(tbody td):not(:where([class~="not-prose"] *)) { + vertical-align: baseline; +} + +.prose :where(tfoot):not(:where([class~="not-prose"] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} + +.prose :where(tfoot td):not(:where([class~="not-prose"] *)) { + vertical-align: top; +} + +.prose { + --tw-prose-body: #374151; + --tw-prose-headings: #111827; + --tw-prose-lead: #4b5563; + --tw-prose-links: #111827; + --tw-prose-bold: #111827; + --tw-prose-counters: #6b7280; + --tw-prose-bullets: #d1d5db; + --tw-prose-hr: #e5e7eb; + --tw-prose-quotes: #111827; + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-code: #111827; + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: #d1d5db; + --tw-prose-td-borders: #e5e7eb; + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.75; +} + +.prose :where(video):not(:where([class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(figure):not(:where([class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(li):not(:where([class~="not-prose"] *)) { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"] *)) { + padding-left: 0.375em; +} + +.prose :where(ul > li):not(:where([class~="not-prose"] *)) { + padding-left: 0.375em; +} + +.prose :where(.prose > ul > li p):not(:where([class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(.prose > ul > li > *:first-child):not(:where([class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ul > li > *:last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(.prose > ol > li > *:first-child):not(:where([class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ol > li > *:last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(hr + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h2 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h3 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h4 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(thead th:first-child):not(:where([class~="not-prose"] *)) { + padding-left: 0; +} + +.prose :where(thead th:last-child):not(:where([class~="not-prose"] *)) { + padding-right: 0; +} + +.prose :where(tbody td, tfoot td):not(:where([class~="not-prose"] *)) { + padding-top: 0.5714286em; + padding-right: 0.5714286em; + padding-bottom: 0.5714286em; + padding-left: 0.5714286em; +} + +.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"] *)) { + padding-left: 0; +} + +.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"] *)) { + padding-right: 0; +} + +.prose :where(.prose > :first-child):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(.prose > :last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 0; +} + +.prose-lg { + font-size: 1.125rem; + line-height: 1.7777778; +} + +.prose-lg :where(p):not(:where([class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; +} + +.prose-lg :where([class~="lead"]):not(:where([class~="not-prose"] *)) { + font-size: 1.2222222em; + line-height: 1.4545455; + margin-top: 1.0909091em; + margin-bottom: 1.0909091em; +} + +.prose-lg :where(blockquote):not(:where([class~="not-prose"] *)) { + margin-top: 1.6666667em; + margin-bottom: 1.6666667em; + padding-left: 1em; +} + +.prose-lg :where(h1):not(:where([class~="not-prose"] *)) { + font-size: 2.6666667em; + margin-top: 0; + margin-bottom: 0.8333333em; + line-height: 1; +} + +.prose-lg :where(h2):not(:where([class~="not-prose"] *)) { + font-size: 1.6666667em; + margin-top: 1.8666667em; + margin-bottom: 1.0666667em; + line-height: 1.3333333; +} + +.prose-lg :where(h3):not(:where([class~="not-prose"] *)) { + font-size: 1.3333333em; + margin-top: 1.6666667em; + margin-bottom: 0.6666667em; + line-height: 1.5; +} + +.prose-lg :where(h4):not(:where([class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 0.4444444em; + line-height: 1.5555556; +} + +.prose-lg :where(img):not(:where([class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + +.prose-lg :where(video):not(:where([class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + +.prose-lg :where(figure):not(:where([class~="not-prose"] *)) { + margin-top: 1.7777778em; + margin-bottom: 1.7777778em; +} + +.prose-lg :where(figure > *):not(:where([class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose-lg :where(figcaption):not(:where([class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.5; + margin-top: 1em; +} + +.prose-lg :where(code):not(:where([class~="not-prose"] *)) { + font-size: 0.8888889em; +} + +.prose-lg :where(h2 code):not(:where([class~="not-prose"] *)) { + font-size: 0.8666667em; +} + +.prose-lg :where(h3 code):not(:where([class~="not-prose"] *)) { + font-size: 0.875em; +} + +.prose-lg :where(pre):not(:where([class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.75; + margin-top: 2em; + margin-bottom: 2em; + border-radius: 0.375rem; + padding-top: 1em; + padding-right: 1.5em; + padding-bottom: 1em; + padding-left: 1.5em; +} + +.prose-lg :where(ol):not(:where([class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + padding-left: 1.5555556em; +} + +.prose-lg :where(ul):not(:where([class~="not-prose"] *)) { + margin-top: 1.3333333em; + margin-bottom: 1.3333333em; + padding-left: 1.5555556em; +} + +.prose-lg :where(li):not(:where([class~="not-prose"] *)) { + margin-top: 0.6666667em; + margin-bottom: 0.6666667em; +} + +.prose-lg :where(ol > li):not(:where([class~="not-prose"] *)) { + padding-left: 0.4444444em; +} + +.prose-lg :where(ul > li):not(:where([class~="not-prose"] *)) { + padding-left: 0.4444444em; +} + +.prose-lg :where(.prose-lg > ul > li p):not(:where([class~="not-prose"] *)) { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; +} + +.prose-lg :where(.prose-lg > ul > li > *:first-child):not(:where([class~="not-prose"] *)) { + margin-top: 1.3333333em; +} + +.prose-lg :where(.prose-lg > ul > li > *:last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 1.3333333em; +} + +.prose-lg :where(.prose-lg > ol > li > *:first-child):not(:where([class~="not-prose"] *)) { + margin-top: 1.3333333em; +} + +.prose-lg :where(.prose-lg > ol > li > *:last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 1.3333333em; +} + +.prose-lg :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"] *)) { + margin-top: 0.8888889em; + margin-bottom: 0.8888889em; +} + +.prose-lg :where(hr):not(:where([class~="not-prose"] *)) { + margin-top: 3.1111111em; + margin-bottom: 3.1111111em; +} + +.prose-lg :where(hr + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose-lg :where(h2 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose-lg :where(h3 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose-lg :where(h4 + *):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose-lg :where(table):not(:where([class~="not-prose"] *)) { + font-size: 0.8888889em; + line-height: 1.5; +} + +.prose-lg :where(thead th):not(:where([class~="not-prose"] *)) { + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; +} + +.prose-lg :where(thead th:first-child):not(:where([class~="not-prose"] *)) { + padding-left: 0; +} + +.prose-lg :where(thead th:last-child):not(:where([class~="not-prose"] *)) { + padding-right: 0; +} + +.prose-lg :where(tbody td, tfoot td):not(:where([class~="not-prose"] *)) { + padding-top: 0.75em; + padding-right: 0.75em; + padding-bottom: 0.75em; + padding-left: 0.75em; +} + +.prose-lg :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"] *)) { + padding-left: 0; +} + +.prose-lg :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"] *)) { + padding-right: 0; +} + +.prose-lg :where(.prose-lg > :first-child):not(:where([class~="not-prose"] *)) { + margin-top: 0; +} + +.prose-lg :where(.prose-lg > :last-child):not(:where([class~="not-prose"] *)) { + margin-bottom: 0; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.my-8 { + margin-top: 2rem; + margin-bottom: 2rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-24 { + margin-bottom: 6rem; +} + +.mb-3 { + margin-bottom: 0.75rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-3 { + margin-left: 0.75rem; +} + +.mt-16 { + margin-top: 4rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.block { + display: block; +} + +.min-h-screen { + min-height: 100vh; +} + +.max-w-7xl { + max-width: 80rem; +} + +.max-w-\[625px\] { + max-width: 625px; +} + +.max-w-\[900px\] { + max-width: 900px; +} + +.max-w-none { + max-width: none; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-b-8 { + border-bottom-width: 8px; +} + +.border-slate-200 { + --tw-border-opacity: 1; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); +} + +.border-tram-green { + --tw-border-opacity: 1; + border-color: rgb(26 156 29 / var(--tw-border-opacity)); +} + +.bg-tram-green-light { + --tw-bg-opacity: 1; + background-color: rgb(246 245 233 / var(--tw-bg-opacity)); +} + +.bg-gradient-to-br { + background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); +} + +.bg-no-repeat { + background-repeat: no-repeat; +} + +.px-1 { + padding-left: 0.25rem; + padding-right: 0.25rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.text-center { + text-align: center; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-medium { + font-weight: 500; +} + +.leading-normal { + line-height: 1.5; +} + +.tracking-wide { + letter-spacing: 0.025em; +} + +.underline { + text-decoration-line: underline; +} + +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.grayscale { + --tw-grayscale: grayscale(100%); + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.filter { + filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.prose-h1\:text-base :is(:where(h1):not(:where([class~="not-prose"] *))) { + font-size: 1rem; + line-height: 1.5rem; +} + +.prose-h1\:font-bold :is(:where(h1):not(:where([class~="not-prose"] *))) { + font-weight: 700; +} + +.prose-p\:font-serif :is(:where(p):not(:where([class~="not-prose"] *))) { + font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +.prose-a\:border-b-\[1px\] :is(:where(a):not(:where([class~="not-prose"] *))) { + border-bottom-width: 1px; +} + +.prose-a\:border-slate-500 :is(:where(a):not(:where([class~="not-prose"] *))) { + --tw-border-opacity: 1; + border-color: rgb(100 116 139 / var(--tw-border-opacity)); +} + +.prose-a\:font-sans :is(:where(a):not(:where([class~="not-prose"] *))) { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +.prose-a\:no-underline :is(:where(a):not(:where([class~="not-prose"] *))) { + text-decoration-line: none; +} + +.hover\:prose-a\:border-slate-200 :is(:where(a):not(:where([class~="not-prose"] *))):hover { + --tw-border-opacity: 1; + border-color: rgb(226 232 240 / var(--tw-border-opacity)); +} + +.hover\:prose-a\:border-b-slate-200 :is(:where(a):not(:where([class~="not-prose"] *))):hover { + --tw-border-opacity: 1; + border-bottom-color: rgb(226 232 240 / var(--tw-border-opacity)); +} + +.prose-p\:prose-blockquote\:font-sans :is(:where(blockquote):not(:where([class~="not-prose"] *))) :is(:where(p):not(:where([class~="not-prose"] *))) { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +.prose-strong\:font-sans :is(:where(strong):not(:where([class~="not-prose"] *))) { + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +.prose-code\:prose-p\:text-sm :is(:where(p):not(:where([class~="not-prose"] *))) :is(:where(code):not(:where([class~="not-prose"] *))) { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.prose-pre\:prose-code\:prose-a\:text-sm :is(:where(a):not(:where([class~="not-prose"] *))) :is(:where(code):not(:where([class~="not-prose"] *))) :is(:where(pre):not(:where([class~="not-prose"] *))) { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.prose-ol\:font-serif :is(:where(ol):not(:where([class~="not-prose"] *))) { + font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +.prose-ul\:font-serif :is(:where(ul):not(:where([class~="not-prose"] *))) { + font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +@media (min-width: 768px) { + .md\:ml-4 { + margin-left: 1rem; + } + + .md\:mr-4 { + margin-right: 1rem; + } + + .md\:flex { + display: flex; + } + + .md\:flex-shrink-0 { + flex-shrink: 0; + } + + .md\:flex-grow { + flex-grow: 1; + } + + .md\:flex-grow-0 { + flex-grow: 0; + } + + .md\:basis-\[100px\] { + flex-basis: 100px; + } +}