kempo-server 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -55,19 +55,22 @@
55
55
  <h1 class="tc-primary">Kempo Server</h1>
56
56
  <img src="./media/icon128.png" alt="Kempo UI Icon" />
57
57
  </a>
58
- <h3>Getting Started</h3>
59
- <a href="./" class="d-b pq pl">Quick Start</a>
60
- <a href="./routing.html" class="d-b pq pl">Routing</a>
61
- <a href="./request-response.html" class="d-b pq pl">Request & Response</a>
62
- <br /><br />
63
- <h3>Advanced Features</h3>
64
- <a href="configuration.html" class="d-b pq pl">Configuration</a>
65
- <a href="templating.html" class="d-b pq pl">Templating</a>
66
- <a href="middleware.html" class="d-b pq pl">Middleware</a>
67
- <a href="caching.html" class="d-b pq pl">Module Caching</a>
68
- <a href="cli-utils.html" class="d-b pq pl">CLI Utilities</a>
69
- <a href="fs-utils.html" class="d-b pq pl">File System Utilities</a>
70
- <a href="examples.html" class="d-b pq pl">Examples & Demos</a>
58
+
59
+ <h3 class="mt mb0">Advanced Features</h3>
60
+ <a href="configuration.html" class="d-b pq pl">Configuration</a>
61
+ <a href="templating.html" class="d-b pq pl">Templating</a>
62
+ <a href="middleware.html" class="d-b pq pl">Middleware</a>
63
+ <a href="caching.html" class="d-b pq pl">Module Caching</a>
64
+ <a href="cli-utils.html" class="d-b pq pl">CLI Utilities</a>
65
+ <a href="fs-utils.html" class="d-b pq pl">File System Utilities</a>
66
+ <a href="examples.html" class="d-b pq pl">Examples & Demos</a>
67
+
68
+ <h3 class="mt mb0">Getting Started</h3>
69
+ <a href="./" class="d-b pq pl">Quick Start</a>
70
+ <a href="./routing.html" class="d-b pq pl">Routing</a>
71
+ <a href="./request-response.html" class="d-b pq pl">Request & Response</a>
72
+ <br /><br />
73
+
71
74
  </menu>
72
75
  </k-aside>
73
76
  <script src="https://cdn.jsdelivr.net/npm/kempo-ui@0.3.5/src/components/Aside.js" type="module"></script>
@@ -105,8 +108,14 @@
105
108
  <li><a href="#overview">Overview</a></li>
106
109
  <li><a href="#file-types">File Types</a></li>
107
110
  <li><a href="#templates">Templates</a></li>
108
- <li><a href="#pages">Pages</a></li>
111
+ <li><a href="#pages">Pages</a>
112
+ <ul>
113
+ <li><a href="#frontmatter">Frontmatter</a></li>
114
+ </ul>
115
+ </li>
109
116
  <li><a href="#fragments">Fragments</a></li>
117
+ <li><a href="#global-files">Global Files</a></li>
118
+ <li><a href="#fragments-vs-globals">Fragments vs. Global Files</a></li>
110
119
  <li><a href="#variables">Variables</a></li>
111
120
  <li><a href="#conditionals">Conditionals</a></li>
112
121
  <li><a href="#loops">Loops</a></li>
@@ -117,22 +126,23 @@
117
126
  </nav>
118
127
 
119
128
  <h2 id="overview">Overview</h2>
120
- <p>The templating system uses three file types that work together:</p>
129
+ <p>The templating system uses four file types that work together:</p>
121
130
  <ul>
122
131
  <li><strong>Templates</strong> (<code>*.template.html</code>) &mdash; Shared page layouts with named content slots</li>
123
132
  <li><strong>Pages</strong> (<code>*.page.html</code>) &mdash; Individual pages that fill template slots with content</li>
124
133
  <li><strong>Fragments</strong> (<code>*.fragment.html</code>) &mdash; Reusable HTML partials included in templates or other fragments</li>
134
+ <li><strong>Globals</strong> (<code>*.global.html</code>) &mdash; Site-wide content blocks automatically injected into every page render</li>
125
135
  </ul>
126
136
  <p>All three file types are blocked from being served directly by the default <code>disallowedRegex</code> configuration.</p>
127
137
 
128
138
  <h2 id="file-types">File Types</h2>
129
139
  <p>A typical project structure:</p>
130
- <pre><code class="hljs markdown">my-site/<br />├─ default.template.html # Shared layout<br />├─ nav.fragment.html # Reusable navigation<br />├─ footer.fragment.html # Reusable footer<br />├─ index.page.html # Homepage → renders to index.html<br />├─ about.page.html # About → renders to about.html<br />├─ blog/<br />│ ├─ index.page.html # Blog index → blog/index.html<br />│ ├─ post-1.page.html # Blog post → blog/post-1.html<br /></code></pre>
140
+ <pre><code class="hljs markdown">my-site/<br />├─ default.template.html # Shared layout<br />├─ nav.fragment.html # Reusable navigation<br />├─ footer.fragment.html # Reusable footer<br />├─ analytics.global.html # Site-wide scripts injected into every page<br />├─ index.page.html # Homepage → renders to index.html<br />├─ about.page.html # About → renders to about.html<br />├─ blog/<br />│ ├─ index.page.html # Blog index → blog/index.html<br />│ ├─ post-1.page.html # Blog post → blog/post-1.html<br /></code></pre>
131
141
  <p>Templates and fragments are resolved by walking up the directory tree from the page file to the root, so subdirectories can override them by providing their own versions.</p>
132
142
 
133
143
  <h2 id="templates">Templates</h2>
134
144
  <p>A template defines the shared HTML structure for your pages. Use <code>&lt;location&gt;</code> tags to define named content slots that pages will fill.</p>
135
- <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- default.template.html --&gt;</span><br /><span class="hljs-meta">&lt;!DOCTYPE html&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Templating - Kempo Server<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">fragment</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"nav"</span> /&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Templating<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">location</span> /&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">location</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"scripts"</span>&gt;</span><br /> <span class="hljs-comment">&lt;!-- default scripts if page doesn't provide any --&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">location</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></code></pre>
145
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- default.template.html --&gt;</span><br /><span class="hljs-meta">&lt;!DOCTYPE html&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>&#123;&#123;title&#125;&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">fragment</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"nav"</span> /&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>&#123;&#123;pageName&#125;&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">location</span> /&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">location</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"scripts"</span>&gt;</span><br /> <span class="hljs-comment">&lt;!-- default scripts if page doesn't provide any --&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">location</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></code></pre>
136
146
 
137
147
  <h3>Location Tags</h3>
138
148
  <p>Locations are named slots in a template that pages fill with content. The <code>name</code> attribute is optional &mdash; a <code>&lt;location&gt;</code> without a name defaults to <code>"default"</code>.</p>
@@ -143,15 +153,22 @@
143
153
  </ul>
144
154
 
145
155
  <h2 id="pages">Pages</h2>
146
- <p>A page file wraps its content in a <code>&lt;page&gt;</code> root element and uses <code>&lt;content&gt;</code> blocks to fill the template's locations. The <code>location</code> attribute is optional &mdash; a <code>&lt;content&gt;</code> block without one targets the <code>"default"</code> location. Multiple <code>&lt;content&gt;</code> blocks targeting the same location are concatenated in order.</p>
156
+ <p>A page file wraps its content in a <code>&lt;page&gt;</code> root element and uses <code>&lt;content&gt;</code> blocks to fill the template's locations. The <code>location</code> attribute is optional &mdash; a <code>&lt;content&gt;</code> block without one targets the <code>"default"</code> location. Multiple <code>&lt;content&gt;</code> blocks targeting the same location are merged and sorted by <code>priority</code> (higher first, default <code>0</code>).</p>
147
157
  <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- about.page.html → renders to about.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">page</span> <span class="hljs-attr">pageName</span>=<span class="hljs-string">"About Us"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"About - My Site"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">content</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Welcome to our about page.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">page</span>&gt;</span></code></pre>
148
158
 
149
159
  <h3>Page Attributes</h3>
150
- <p>Attributes on the <code>&lt;page&gt;</code> tag become template variables. In the example above, <code>Templating</code> resolves to <code>About Us</code> and <code>Templating - Kempo Server</code> resolves to <code>About - My Site</code>.</p>
160
+ <p>Attributes on the <code>&lt;page&gt;</code> tag become template variables. In the example above, <code>&#123;&#123;pageName&#125;&#125;</code> resolves to <code>About Us</code> and <code>&#123;&#123;title&#125;&#125;</code> resolves to <code>About - My Site</code>.</p>
151
161
  <p>The special <code>template</code> attribute selects which template to use. It defaults to <code>default</code>, which looks for <code>default.template.html</code>.</p>
152
162
  <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- Uses blog.template.html instead of default.template.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">page</span> <span class="hljs-attr">template</span>=<span class="hljs-string">"blog"</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"My Post"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">location</span>=<span class="hljs-string">"main"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">page</span>&gt;</span></code></pre>
153
163
 
154
- <h2 id="fragments">Fragments</h2>
164
+ <h3 id="frontmatter">Frontmatter</h3>
165
+ <p>Any content before the opening <code>&lt;page&gt;</code> tag is ignored by the renderer. This makes it a natural place for author metadata, description, dates, or any notes that belong with the file but shouldn't appear in the output &mdash; similar to frontmatter in other static site generators.</p>
166
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!--
167
+ author: Dustin Poissant
168
+ date: 2026-04-10
169
+ description: Overview of the routing system
170
+ --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">page</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"Routing"</span>&gt;</span><br /> ...<br /><span class="hljs-tag">&lt;/<span class="hljs-name">page</span>&gt;</span></code></pre>
171
+ <p>The comment is purely for the author &mdash; nothing before <code>&lt;page&gt;</code> is parsed or rendered.</p>
155
172
  <p>Fragments are reusable HTML partials. Include them in templates or other fragments using the <code>&lt;fragment&gt;</code> tag.</p>
156
173
  <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- nav.fragment.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">fragment</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./about.html"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">fragment</span>&gt;</span></code></pre>
157
174
  <p>Include it in a template:</p>
@@ -165,10 +182,44 @@
165
182
  <li>A subdirectory can provide its own version to override the parent</li>
166
183
  </ul>
167
184
 
185
+ <h2 id="global-files">Global Files</h2>
186
+ <p>Any file named <code>*.global.html</code> anywhere under the root directory is automatically loaded and its <code>&lt;content&gt;</code> blocks are injected into every page render. This is the right place to put site-wide scripts, meta tags, nav links, or anything else that should appear across all pages without touching every template or page file.</p>
187
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- analytics.global.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">location</span>=<span class="hljs-string">"scripts"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"analytics.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span></code></pre>
188
+ <p>The template just needs a matching <code>&lt;location&gt;</code> tag &mdash; the global file fills it automatically on every page.</p>
189
+
190
+ <h3>Priority</h3>
191
+ <p>When multiple <code>&lt;content&gt;</code> blocks &mdash; from global files, multiple global files, or the page itself &mdash; target the same location, a <code>priority</code> attribute controls their output order. Higher numbers come first. The default is <code>0</code>.</p>
192
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- framework.global.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">location</span>=<span class="hljs-string">"scripts"</span> <span class="hljs-attr">priority</span>=<span class="hljs-string">"10"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"framework.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- analytics.global.html — priority defaults to 0 --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">location</span>=<span class="hljs-string">"scripts"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"analytics.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span></code></pre>
193
+ <p>Output: <code>framework.js</code> first (priority 10), then <code>analytics.js</code> (priority 0). The same <code>priority</code> attribute works on <code>&lt;content&gt;</code> blocks in page files too, so a page can insert content before or after global content in the same location.</p>
194
+
195
+ <h3>Locations Inside Page Content</h3>
196
+ <p>You can place <code>&lt;location&gt;</code> tags inside a page's <code>&lt;content&gt;</code> block. Global content fills those locations before the block is inserted into the template. This lets a page pull in a global snippet at a specific spot without knowing what the snippet contains.</p>
197
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- nav-links.global.html --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">content</span> <span class="hljs-attr">location</span>=<span class="hljs-string">"links"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"./about.html"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">content</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- nav.fragment.html — location tag pulls in the global links --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">fragment</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">location</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"links"</span> /&gt;</span><br /> <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">fragment</span>&gt;</span></code></pre>
198
+
199
+ <h2 id="fragments-vs-globals">Fragments vs. Global Files</h2>
200
+ <p>Fragments and global files both let you share HTML across pages, but they work in opposite directions.</p>
201
+ <p><strong>Fragments are a pull.</strong> The template (or another fragment) explicitly asks for one thing by name: <code>&lt;fragment name="nav" /&gt;</code>. The fragment file is a single reusable block of markup. Only what is asked for gets included, and where it lands is decided entirely by the caller.</p>
202
+ <p><strong>Global files are a push.</strong> A <code>*.global.html</code> file declares content and targets a location, and that content is automatically injected into every page render without the template needing to know it exists. You can have many global files each contributing to the same location, and a page can add its own content to that same location alongside them. The final output is all of those contributions merged and ordered by <code>priority</code>.</p>
203
+ <div class="table-wrapper mb">
204
+ <table>
205
+ <thead>
206
+ <tr><th></th><th>Fragments</th><th>Global Files</th></tr>
207
+ </thead>
208
+ <tbody>
209
+ <tr><td>Direction</td><td>Pull — caller decides what to include</td><td>Push — file injects itself automatically</td></tr>
210
+ <tr><td>How many per location</td><td>One (the named file)</td><td>Many — all globals targeting the same location are merged</td></tr>
211
+ <tr><td>Page can contribute too?</td><td>No — the fragment replaces the tag entirely</td><td>Yes — page <code>&lt;content&gt;</code> blocks merge with global content</td></tr>
212
+ <tr><td>Requires template change?</td><td>Yes — caller must add <code>&lt;fragment name="..." /&gt;</code></td><td>No — just add a <code>&lt;location&gt;</code> and the globals fill it</td></tr>
213
+ <tr><td>Good for</td><td>Reusable structural blocks (nav, footer, sidebar)</td><td>Site-wide injections (scripts, meta tags, nav links)</td></tr>
214
+ </tbody>
215
+ </table>
216
+ </div>
217
+
168
218
  <h2 id="variables">Variables</h2>
169
- <p>Use <code></code> syntax to insert dynamic values into templates and fragments.</p>
219
+ <p>Use <code>&#123;&#123;variableName&#125;&#125;</code> syntax to insert dynamic values into templates and fragments.</p>
170
220
 
171
221
  <h3>Built-in Variables</h3>
222
+ <div class="table-wrapper mb">
172
223
  <table>
173
224
  <thead>
174
225
  <tr>
@@ -177,19 +228,20 @@
177
228
  </tr>
178
229
  </thead>
179
230
  <tbody>
180
- <tr><td><code>./</code></td><td>Relative path from the page to the root directory (e.g. <code>./</code>, <code>../</code>, <code>../../</code>)</td></tr>
181
- <tr><td><code>2026</code></td><td>Current four-digit year</td></tr>
182
- <tr><td><code>2026-04-09</code></td><td>Current date in ISO format (<code>YYYY-MM-DD</code>)</td></tr>
183
- <tr><td><code>2026-04-09T19:42:42.690Z</code></td><td>Full ISO 8601 datetime string</td></tr>
184
- <tr><td><code>1775763762690</code></td><td>Unix timestamp in milliseconds</td></tr>
185
- <tr><td><code></code></td><td>Version from the root <code>package.json</code></td></tr>
186
- <tr><td><code></code></td><td>Value of <code>NODE_ENV</code></td></tr>
231
+ <tr><td><code>&#123;&#123;pathToRoot&#125;&#125;</code></td><td>Relative path from the page to the root directory (e.g. <code>./</code>, <code>../</code>, <code>../../</code>)</td></tr>
232
+ <tr><td><code>&#123;&#123;year&#125;&#125;</code></td><td>Current four-digit year</td></tr>
233
+ <tr><td><code>&#123;&#123;date&#125;&#125;</code></td><td>Current date in ISO format (<code>YYYY-MM-DD</code>)</td></tr>
234
+ <tr><td><code>&#123;&#123;datetime&#125;&#125;</code></td><td>Full ISO 8601 datetime string</td></tr>
235
+ <tr><td><code>&#123;&#123;timestamp&#125;&#125;</code></td><td>Unix timestamp in milliseconds</td></tr>
236
+ <tr><td><code>&#123;&#123;version&#125;&#125;</code></td><td>Version from the root <code>package.json</code></td></tr>
237
+ <tr><td><code>&#123;&#123;env&#125;&#125;</code></td><td>Value of <code>NODE_ENV</code></td></tr>
187
238
  </tbody>
188
239
  </table>
240
+ </div>
189
241
 
190
242
  <h3>Page Attributes as Variables</h3>
191
243
  <p>Any attribute on the <code>&lt;page&gt;</code> tag is available as a variable in the template:</p>
192
- <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- page file --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">page</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"My Page"</span> <span class="hljs-attr">author</span>=<span class="hljs-string">"Dustin"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">page</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- template file --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Templating - Kempo Server<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">""</span> /&gt;</span></code></pre>
244
+ <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- page file --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">page</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"My Page"</span> <span class="hljs-attr">author</span>=<span class="hljs-string">"Dustin"</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">page</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- template file --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>&#123;&#123;title&#125;&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"&#123;&#123;author&#125;&#125;"</span> /&gt;</span></code></pre>
193
245
 
194
246
  <h3>Globals and State</h3>
195
247
  <p>Additional variables can be provided through the <code>globals</code> and <code>state</code> configuration objects. Globals and state are merged with page attributes, with page attributes taking priority.</p>
@@ -198,7 +250,7 @@
198
250
 
199
251
  <h3>Dot-Path Access</h3>
200
252
  <p>Variables support dot notation for nested object access:</p>
201
- <pre><code class="hljs xml"><br /></code></pre>
253
+ <pre><code class="hljs xml">&#123;&#123;site.name&#125;&#125;<br />&#123;&#123;author.email&#125;&#125;</code></pre>
202
254
 
203
255
  <h2 id="conditionals">Conditionals</h2>
204
256
  <p>Use <code>&lt;if&gt;</code> blocks to conditionally include content based on variable values.</p>
@@ -206,6 +258,7 @@
206
258
 
207
259
  <h3>Supported Operators</h3>
208
260
  <p>Conditions support a full expression syntax:</p>
261
+ <div class="table-wrapper mb">
209
262
  <table>
210
263
  <thead>
211
264
  <tr>
@@ -223,13 +276,14 @@
223
276
  <tr><td><code>( )</code></td><td>Grouping</td></tr>
224
277
  </tbody>
225
278
  </table>
279
+ </div>
226
280
 
227
281
  <h3>Examples</h3>
228
282
  <pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- Truthy check --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">if</span> <span class="hljs-attr">condition</span>=<span class="hljs-string">"isLoggedIn"</span>&gt;</span>Welcome back!<span class="hljs-tag">&lt;/<span class="hljs-name">if</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- Negation --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">if</span> <span class="hljs-attr">condition</span>=<span class="hljs-string">"!isLoggedIn"</span>&gt;</span>Please log in.<span class="hljs-tag">&lt;/<span class="hljs-name">if</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- String comparison --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">if</span> <span class="hljs-attr">condition</span>=<span class="hljs-string">"env === 'production'"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"analytics.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">if</span>&gt;</span><br /><br /><span class="hljs-comment">&lt;!-- Compound conditions --&gt;</span><br /><span class="hljs-tag">&lt;<span class="hljs-name">if</span> <span class="hljs-attr">condition</span>=<span class="hljs-string">"isAdmin &amp;&amp; hasPermission"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/admin"</span>&gt;</span>Admin Panel<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">if</span>&gt;</span></code></pre>
229
283
 
230
284
  <h2 id="loops">Loops</h2>
231
285
  <p>Use <code>&lt;foreach&gt;</code> blocks to iterate over arrays.</p>
232
- <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">foreach</span> <span class="hljs-attr">in</span>=<span class="hljs-string">"navLinks"</span> <span class="hljs-attr">as</span>=<span class="hljs-string">"link"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">""</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">foreach</span>&gt;</span></code></pre>
286
+ <pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">foreach</span> <span class="hljs-attr">in</span>=<span class="hljs-string">"navLinks"</span> <span class="hljs-attr">as</span>=<span class="hljs-string">"link"</span>&gt;</span><br /> <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"&#123;&#123;link.url&#125;&#125;"</span>&gt;</span>&#123;&#123;link.label&#125;&#125;<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><br /><span class="hljs-tag">&lt;/<span class="hljs-name">foreach</span>&gt;</span></code></pre>
233
287
  <p>The <code>in</code> attribute references an array variable and <code>as</code> names the loop variable. The loop variable supports dot-path access for object items.</p>
234
288
 
235
289
  <p>Provide the array through globals or state:</p>
@@ -259,6 +313,7 @@
259
313
 
260
314
  <h2 id="configuration">Configuration</h2>
261
315
  <p>All templating options live under the <code>templating</code> key in your configuration file.</p>
316
+ <div class="table-wrapper mb">
262
317
  <table>
263
318
  <thead>
264
319
  <tr>
@@ -276,6 +331,7 @@
276
331
  <tr><td><code>maxFragmentDepth</code></td><td><code>10</code></td><td>Maximum fragment nesting depth</td></tr>
277
332
  </tbody>
278
333
  </table>
334
+ </div>
279
335
 
280
336
  <h3>Development vs Production</h3>
281
337
  <p>A common pattern is to use separate config files:</p>
@@ -0,0 +1,10 @@
1
+ <content location="links">
2
+ <h3 class="mt mb0">Advanced Features</h3>
3
+ <a href="configuration.html" class="d-b pq pl">Configuration</a>
4
+ <a href="templating.html" class="d-b pq pl">Templating</a>
5
+ <a href="middleware.html" class="d-b pq pl">Middleware</a>
6
+ <a href="caching.html" class="d-b pq pl">Module Caching</a>
7
+ <a href="cli-utils.html" class="d-b pq pl">CLI Utilities</a>
8
+ <a href="fs-utils.html" class="d-b pq pl">File System Utilities</a>
9
+ <a href="examples.html" class="d-b pq pl">Examples & Demos</a>
10
+ </content>
@@ -24,6 +24,7 @@
24
24
  }</code></pre>
25
25
 
26
26
  <h2>Configuration Options</h2>
27
+ <div class="table-wrapper mb">
27
28
  <table>
28
29
  <thead>
29
30
  <tr>
@@ -84,6 +85,7 @@
84
85
  </tr>
85
86
  </tbody>
86
87
  </table>
88
+ </div>
87
89
 
88
90
  <h2>Environment-Specific Configuration</h2>
89
91
  <p>Use separate configuration files for different environments instead of nested objects:</p>
@@ -207,6 +207,7 @@
207
207
  <pre><code class="hljs json">{<br /> <span class="hljs-attr">"cache"</span>: {<br /> <span class="hljs-attr">"enabled"</span>: <span class="hljs-literal">true</span>,<br /> <span class="hljs-attr">"maxSize"</span>: <span class="hljs-number">1000</span>,<br /> <span class="hljs-attr">"ttlMs"</span>: <span class="hljs-number">3600000</span>,<br /> <span class="hljs-attr">"maxMemoryUsageMB"</span>: <span class="hljs-number">500</span>,<br /> <span class="hljs-attr">"checkIntervalMs"</span>: <span class="hljs-number">30000</span>,<br /> <span class="hljs-attr">"fileWatching"</span>: <span class="hljs-literal">true</span><br /> }<br />}</code></pre>
208
208
 
209
209
  <h4>Cache Configuration Options</h4>
210
+ <div class="table-wrapper mb">
210
211
  <table>
211
212
  <thead>
212
213
  <tr>
@@ -255,6 +256,7 @@
255
256
  </tr>
256
257
  </tbody>
257
258
  </table>
259
+ </div>
258
260
 
259
261
  <h4>Environment-Specific Cache Settings</h4>
260
262
  <h5>Development (.config.dev.json)</h5>
@@ -0,0 +1,7 @@
1
+ <content location="links">
2
+ <h3 class="mt mb0">Getting Started</h3>
3
+ <a href="./" class="d-b pq pl">Quick Start</a>
4
+ <a href="./routing.html" class="d-b pq pl">Routing</a>
5
+ <a href="./request-response.html" class="d-b pq pl">Request & Response</a>
6
+ <br /><br />
7
+ </content>
@@ -1,3 +1,6 @@
1
+ <!--
2
+ Is this frontmatter allowed in our templating system?
3
+ -->
1
4
  <page pageName="Kempo Server" title="Kempo Server">
2
5
  <content>
3
6
  <div class="ta-center">
@@ -32,19 +32,7 @@
32
32
  <h1 class="tc-primary">Kempo Server</h1>
33
33
  <img src="./media/icon128.png" alt="Kempo UI Icon" />
34
34
  </a>
35
- <h3>Getting Started</h3>
36
- <a href="./" class="d-b pq pl">Quick Start</a>
37
- <a href="./routing.html" class="d-b pq pl">Routing</a>
38
- <a href="./request-response.html" class="d-b pq pl">Request & Response</a>
39
- <br /><br />
40
- <h3>Advanced Features</h3>
41
- <a href="configuration.html" class="d-b pq pl">Configuration</a>
42
- <a href="templating.html" class="d-b pq pl">Templating</a>
43
- <a href="middleware.html" class="d-b pq pl">Middleware</a>
44
- <a href="caching.html" class="d-b pq pl">Module Caching</a>
45
- <a href="cli-utils.html" class="d-b pq pl">CLI Utilities</a>
46
- <a href="fs-utils.html" class="d-b pq pl">File System Utilities</a>
47
- <a href="examples.html" class="d-b pq pl">Examples & Demos</a>
35
+ <location name="links" />
48
36
  </menu>
49
37
  </k-aside>
50
38
  <script src="https://cdn.jsdelivr.net/npm/kempo-ui@0.3.5/src/components/Aside.js" type="module"></script>
@@ -27,6 +27,7 @@
27
27
 
28
28
  <h3 id="body-parsing">Body Parsing</h3>
29
29
  <p><code>request.body</code> is eagerly parsed before your route handler runs. The parsing strategy depends on the <code>Content-Type</code> header:</p>
30
+ <div class="table-wrapper mb">
30
31
  <table>
31
32
  <thead>
32
33
  <tr><th>Content-Type</th><th><code>request.body</code> value</th></tr>
@@ -38,6 +39,7 @@
38
39
  <tr><td>Any other Content-Type</td><td>Raw string</td></tr>
39
40
  </tbody>
40
41
  </table>
42
+ </div>
41
43
  <p>The convenience methods <code>request.json()</code>, <code>request.text()</code>, and <code>request.buffer()</code> still exist and return promises for backward compatibility, but they resolve immediately from the cached body.</p>
42
44
 
43
45
  <h2>Response Object</h2>