@pagenary/publisher 2026.5.0

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.
Files changed (147) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +337 -0
  3. package/bin/pagenary.mjs +116 -0
  4. package/build.config.json +5 -0
  5. package/package.json +66 -0
  6. package/scripts/build-site.js +87 -0
  7. package/scripts/build-tenants.js +3569 -0
  8. package/scripts/build.js +99 -0
  9. package/scripts/generate-sections.js +41 -0
  10. package/scripts/lib/seo-generator.js +558 -0
  11. package/scripts/lint-content.js +62 -0
  12. package/scripts/seo-smoke.js +94 -0
  13. package/scripts/serve.js +142 -0
  14. package/site/app.js +1 -0
  15. package/site/index.html +57 -0
  16. package/site/lib/categories.js +1 -0
  17. package/site/lib/export.js +1 -0
  18. package/site/lib/manifest-utils.js +1 -0
  19. package/site/lib/router.js +1 -0
  20. package/site/lib/search.js +1 -0
  21. package/site/llms.txt +22 -0
  22. package/site/manifest.js +132 -0
  23. package/site/mermaid-init.js +1 -0
  24. package/site/pages/api.html +339 -0
  25. package/site/pages/architecture.html +303 -0
  26. package/site/pages/deployment.html +282 -0
  27. package/site/pages/developer-guide.html +157 -0
  28. package/site/pages/extending.html +135 -0
  29. package/site/pages/quickstart.html +318 -0
  30. package/site/pages/seo-strategy.html +121 -0
  31. package/site/pages/tenant-config.html +519 -0
  32. package/site/pages/welcome.html +116 -0
  33. package/site/robots.txt +10 -0
  34. package/site/sections/api.js +3 -0
  35. package/site/sections/architecture.js +3 -0
  36. package/site/sections/deployment.js +3 -0
  37. package/site/sections/developer-guide.js +3 -0
  38. package/site/sections/extending.js +3 -0
  39. package/site/sections/quickstart.js +3 -0
  40. package/site/sections/section-templates.js +1 -0
  41. package/site/sections/seo-strategy.js +3 -0
  42. package/site/sections/tenant-config.js +3 -0
  43. package/site/sections/welcome.js +3 -0
  44. package/site/seo.js +1 -0
  45. package/site/sitemap.xml +63 -0
  46. package/site/styles.css +1982 -0
  47. package/site/syntax-highlight.js +1 -0
  48. package/src/app.js +988 -0
  49. package/src/index.html +56 -0
  50. package/src/lib/categories.js +55 -0
  51. package/src/lib/export.js +195 -0
  52. package/src/lib/manifest-utils.js +69 -0
  53. package/src/lib/router.js +44 -0
  54. package/src/lib/search.js +151 -0
  55. package/src/manifest.js +246 -0
  56. package/src/mermaid-init.js +207 -0
  57. package/src/sections/archive-future-roadmap.js +7 -0
  58. package/src/sections/archive-initiative-alpha.js +7 -0
  59. package/src/sections/archive-milestone-records.js +7 -0
  60. package/src/sections/archive-timeline-overview.js +7 -0
  61. package/src/sections/core-technology-compliance-frameworks.js +7 -0
  62. package/src/sections/core-technology-coordination-model.js +7 -0
  63. package/src/sections/core-technology-data-definitions.js +7 -0
  64. package/src/sections/core-technology-hardware-integration.js +7 -0
  65. package/src/sections/core-technology-integrity-controls.js +7 -0
  66. package/src/sections/core-technology-network-topology.js +7 -0
  67. package/src/sections/core-technology-operator-requirements.js +7 -0
  68. package/src/sections/core-technology-overview.js +7 -0
  69. package/src/sections/core-technology-service-interfaces.js +7 -0
  70. package/src/sections/core-technology-synchronization-strategy.js +7 -0
  71. package/src/sections/core-technology-system-foundation.js +7 -0
  72. package/src/sections/developers-api-credentials.js +7 -0
  73. package/src/sections/developers-api-operations.js +7 -0
  74. package/src/sections/developers-api-reference.js +7 -0
  75. package/src/sections/developers-api-websocket.js +7 -0
  76. package/src/sections/developers-automation-blueprints.js +7 -0
  77. package/src/sections/developers-automation-modules.js +7 -0
  78. package/src/sections/developers-automation-patterns.js +7 -0
  79. package/src/sections/developers-deployment-playbook.js +7 -0
  80. package/src/sections/developers-overview.js +7 -0
  81. package/src/sections/developers-scheduling-patterns.js +7 -0
  82. package/src/sections/developers-sdk-go.js +7 -0
  83. package/src/sections/developers-sdk-javascript.js +7 -0
  84. package/src/sections/developers-sdk-python.js +7 -0
  85. package/src/sections/developers-sdk-rust.js +7 -0
  86. package/src/sections/developers-sdks.js +7 -0
  87. package/src/sections/developers-solution-examples.js +7 -0
  88. package/src/sections/developers-testing-framework.js +7 -0
  89. package/src/sections/getting-started-architecture-basics.js +7 -0
  90. package/src/sections/getting-started-introduction.js +7 -0
  91. package/src/sections/getting-started-performance-overview.js +7 -0
  92. package/src/sections/governance-community-initiatives.js +7 -0
  93. package/src/sections/governance-dao-overview.js +7 -0
  94. package/src/sections/governance-multi-token.js +7 -0
  95. package/src/sections/governance-overview.js +7 -0
  96. package/src/sections/governance-proposal-process.js +7 -0
  97. package/src/sections/governance-proposals.js +7 -0
  98. package/src/sections/governance-structure.js +7 -0
  99. package/src/sections/governance-token-distribution.js +7 -0
  100. package/src/sections/governance-treasury.js +7 -0
  101. package/src/sections/operations-environment-prep.js +7 -0
  102. package/src/sections/operations-getting-started.js +7 -0
  103. package/src/sections/operations-incentives-guide.js +7 -0
  104. package/src/sections/operations-incentives-strategies.js +7 -0
  105. package/src/sections/operations-incentives.js +7 -0
  106. package/src/sections/operations-infrastructure.js +7 -0
  107. package/src/sections/operations-monitoring.js +7 -0
  108. package/src/sections/operations-overview.js +7 -0
  109. package/src/sections/operations-performance.js +7 -0
  110. package/src/sections/operations-power-infrastructure.js +7 -0
  111. package/src/sections/operations-setup-guide.js +7 -0
  112. package/src/sections/operations-sync-setup.js +7 -0
  113. package/src/sections/products-flagship-solution.js +7 -0
  114. package/src/sections/products-solution-library.js +7 -0
  115. package/src/sections/resources-brand-assets.js +7 -0
  116. package/src/sections/resources-faq.js +7 -0
  117. package/src/sections/resources-glossary.js +7 -0
  118. package/src/sections/resources-research-papers.js +7 -0
  119. package/src/sections/section-templates.js +873 -0
  120. package/src/sections/security-audits.js +7 -0
  121. package/src/sections/security-best-practices.js +7 -0
  122. package/src/sections/security-bug-bounty.js +7 -0
  123. package/src/sections/security-incident-response.js +7 -0
  124. package/src/sections/security-overview.js +7 -0
  125. package/src/sections/technical-architecture.js +7 -0
  126. package/src/sections/technical-whitepaper.js +7 -0
  127. package/src/sections/tutorial-automation-bot.js +7 -0
  128. package/src/sections/tutorial-build-first-integration.js +7 -0
  129. package/src/sections/tutorial-deploy-automation.js +7 -0
  130. package/src/sections/tutorial-event-driven-experience.js +7 -0
  131. package/src/sections/tutorial-operations-onboarding.js +7 -0
  132. package/src/sections/tutorial-systems-integration.js +7 -0
  133. package/src/sections/tutorials-overview.js +7 -0
  134. package/src/sections/use-case-connected-devices.js +7 -0
  135. package/src/sections/use-case-digital-auctions.js +7 -0
  136. package/src/sections/use-case-financial-automation.js +7 -0
  137. package/src/sections/use-case-interactive-media.js +7 -0
  138. package/src/sections/use-case-realtime-execution.js +7 -0
  139. package/src/sections/use-case-research-analytics.js +7 -0
  140. package/src/sections/use-case-supply-operations.js +7 -0
  141. package/src/sections/use-cases-overview.js +7 -0
  142. package/src/sections/welcome-overview.js +7 -0
  143. package/src/seo.js +90 -0
  144. package/src/styles.css +1982 -0
  145. package/src/syntax-highlight.js +90 -0
  146. package/tenants.json.example +68 -0
  147. package/tenants.schema.json +231 -0
@@ -0,0 +1,339 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>API Reference | Pagenary Docs</title>
7
+ <meta name="description" content="Module-level documentation for the publisher internals." />
8
+ <link rel="canonical" href="/#api" />
9
+
10
+ <!-- Open Graph -->
11
+ <meta property="og:title" content="API Reference" />
12
+ <meta property="og:description" content="Module-level documentation for the publisher internals." />
13
+ <meta property="og:type" content="article" />
14
+ <meta property="og:url" content="/#api" />
15
+
16
+ <!-- Twitter Card -->
17
+ <meta name="twitter:card" content="summary" />
18
+ <meta name="twitter:title" content="API Reference" />
19
+ <meta name="twitter:description" content="Module-level documentation for the publisher internals." />
20
+
21
+ <!-- Structured Data -->
22
+ <script type="application/ld+json">
23
+ [
24
+ {
25
+ "@context": "https://schema.org",
26
+ "@type": "TechArticle",
27
+ "headline": "API Reference",
28
+ "description": "Module-level documentation for the publisher internals.",
29
+ "url": "/#api",
30
+ "dateModified": "2026-05-26",
31
+ "mainEntityOfPage": {
32
+ "@type": "WebPage",
33
+ "@id": "/#api"
34
+ },
35
+ "isPartOf": {
36
+ "@type": "WebSite",
37
+ "name": "Pagenary Docs",
38
+ "url": "/"
39
+ }
40
+ },
41
+ {
42
+ "@context": "https://schema.org",
43
+ "@type": "BreadcrumbList",
44
+ "itemListElement": [
45
+ {
46
+ "@type": "ListItem",
47
+ "position": 1,
48
+ "name": "Home",
49
+ "item": "/"
50
+ },
51
+ {
52
+ "@type": "ListItem",
53
+ "position": 2,
54
+ "name": "Reference",
55
+ "item": "/#reference"
56
+ },
57
+ {
58
+ "@type": "ListItem",
59
+ "position": 3,
60
+ "name": "API Reference",
61
+ "item": "/#api"
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ </script>
67
+
68
+ <!-- Redirect to SPA for JavaScript-enabled browsers -->
69
+ <script>
70
+ if (typeof window !== 'undefined') {
71
+ window.location.replace('/#api');
72
+ }
73
+ </script>
74
+ <noscript>
75
+ <meta http-equiv="refresh" content="0; url=/#api" />
76
+ </noscript>
77
+
78
+ <link rel="stylesheet" href="../styles.css" />
79
+ <style>
80
+ .static-content { max-width: 800px; margin: 0 auto; padding: 2rem; }
81
+ .static-footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #eee; font-size: 0.9rem; color: #666; }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <main class="static-content">
86
+ <article>
87
+ <h1>API Reference</h1>
88
+ <p class="lead">Module-level documentation for the publisher internals.</p>
89
+ <div class="section-body">
90
+ <section class="section doc markdown">
91
+ <div class="doc-content">
92
+ <h1 id="api-reference">API Reference</h1>
93
+ <p>Complete reference for Pagenary Publisher modules and functions.</p>
94
+ <h2 id="core-modules">Core Modules</h2>
95
+ <h3 id="appjs-shell-controller">app.js - Shell Controller</h3>
96
+ <p>Main application controller handling routing, navigation, and UI state.</p>
97
+ <h4 id="functions">Functions</h4>
98
+ <p><strong>`navigate(id: string): void`</strong></p>
99
+ <p>Navigate to a section by ID.</p>
100
+ <pre><code class="language-javascript">navigate(&#39;guides/getting-started&#39;);
101
+ // Updates hash to #/guides/getting-started and renders section</code></pre>
102
+ <p><strong>`handleRoute(): Promise&lt;void&gt;`</strong></p>
103
+ <p>Process current URL hash and render corresponding section.</p>
104
+ <p><strong>`loadSection(entry: SectionEntry): Promise&lt;void&gt;`</strong></p>
105
+ <p>Load and render a section from the manifest.</p>
106
+ <pre><code class="language-javascript">const section = findSection(&#39;welcome&#39;);
107
+ await loadSection(section);</code></pre>
108
+ <p><strong>`updateNavState(activeId: string): void`</strong></p>
109
+ <p>Update navigation UI to reflect active section.</p>
110
+ <p><strong>`openCommandPalette(): void`</strong></p>
111
+ <p>Open the command palette (search interface).</p>
112
+ <p><strong>`closeCommandPalette(): void`</strong></p>
113
+ <p>Close the command palette.</p>
114
+ <hr>
115
+ <h3 id="manifestjs-navigation-registry">manifest.js - Navigation Registry</h3>
116
+ <p>Defines navigation structure and section metadata.</p>
117
+ <h4 id="exports">Exports</h4>
118
+ <p><strong>`MANIFEST: SectionEntry[]`</strong></p>
119
+ <p>Ordered array of sections defining navigation.</p>
120
+ <pre><code class="language-javascript">export const MANIFEST = [
121
+ {
122
+ id: &#39;welcome&#39;,
123
+ title: &#39;Welcome&#39;,
124
+ summary: &#39;Introduction&#39;,
125
+ module: &#39;./sections/welcome.js&#39;
126
+ },
127
+ {
128
+ id: &#39;guides&#39;,
129
+ title: &#39;Guides&#39;,
130
+ subsections: [
131
+ { id: &#39;guides/setup&#39;, title: &#39;Setup&#39;, module: &#39;./sections/guides--setup.js&#39; }
132
+ ]
133
+ }
134
+ ];</code></pre>
135
+ <p><strong>`DEFAULT_SECTION: string`</strong></p>
136
+ <p>ID of the default section (first in manifest).</p>
137
+ <p><strong>`SITE_CONFIG: SiteConfig`</strong></p>
138
+ <p>Site configuration from build.</p>
139
+ <pre><code class="language-javascript">export const SITE_CONFIG = {
140
+ title: &#39;My Docs&#39;,
141
+ description: &#39;Documentation for My Product&#39;,
142
+ brandMark: &#39;MY&#39;,
143
+ brandSub: &#39;DOCS&#39;
144
+ };</code></pre>
145
+ <p><strong>`findSection(id: string): SectionEntry | undefined`</strong></p>
146
+ <p>Look up a section by ID.</p>
147
+ <pre><code class="language-javascript">const section = findSection(&#39;guides/setup&#39;);
148
+ // Returns { id: &#39;guides/setup&#39;, title: &#39;Setup&#39;, ... }</code></pre>
149
+ <p><strong>`getAdjacentSections(id: string): { prev?: SectionEntry, next?: SectionEntry }`</strong></p>
150
+ <p>Get previous and next sections for navigation.</p>
151
+ <hr>
152
+ <h3 id="seojs-metadata-helper">seo.js - Metadata Helper</h3>
153
+ <p>Manages document metadata for SEO.</p>
154
+ <h4 id="functions-2">Functions</h4>
155
+ <p><strong>`updateMetaTags({ title: string, description?: string }): void`</strong></p>
156
+ <p>Update document title and meta description.</p>
157
+ <pre><code class="language-javascript">updateMetaTags({
158
+ title: &#39;Getting Started - My Docs&#39;,
159
+ description: &#39;Learn how to get started with My Product&#39;
160
+ });</code></pre>
161
+ <hr>
162
+ <h2 id="library-modules">Library Modules</h2>
163
+ <h3 id="libsearchjs-full-text-search">lib/search.js - Full-Text Search</h3>
164
+ <p>Search functionality with lazy content indexing.</p>
165
+ <h4 id="functions-3">Functions</h4>
166
+ <p><strong>`escapeRegExp(value: string): string`</strong></p>
167
+ <p>Escape special regex characters.</p>
168
+ <pre><code class="language-javascript">escapeRegExp(&#39;foo.bar&#39;); // &#39;foo\\.bar&#39;</code></pre>
169
+ <p><strong>`flattenManifest(manifest: SectionEntry[]): FlatSection[]`</strong></p>
170
+ <p>Flatten nested manifest into searchable sections.</p>
171
+ <pre><code class="language-javascript">const flat = flattenManifest(MANIFEST);
172
+ // Returns all navigable sections with group info</code></pre>
173
+ <p><strong>`buildSearchIndex(manifest: SectionEntry[]): Promise&lt;IndexedSection[]&gt;`</strong></p>
174
+ <p>Build search index by loading all section modules. Cached after first call.</p>
175
+ <pre><code class="language-javascript">const index = await buildSearchIndex(MANIFEST);
176
+ // Each entry has searchContent: lowercase text for matching</code></pre>
177
+ <p><strong>`filterSections(manifest: SectionEntry[], query: string): FlatSection[]`</strong></p>
178
+ <p>Synchronous title/summary search (no content).</p>
179
+ <pre><code class="language-javascript">const results = filterSections(MANIFEST, &#39;setup&#39;);</code></pre>
180
+ <p><strong>`searchContent(manifest: SectionEntry[], query: string): Promise&lt;IndexedSection[]&gt;`</strong></p>
181
+ <p>Full-text search across all content.</p>
182
+ <pre><code class="language-javascript">const results = await searchContent(MANIFEST, &#39;authentication&#39;);
183
+ // Searches titles, summaries, and full content</code></pre>
184
+ <p><strong>`findPreferredIndex(entries: Section[], currentId: string): number`</strong></p>
185
+ <p>Find index of current section in filtered results.</p>
186
+ <hr>
187
+ <h3 id="librouterjs-hash-routing">lib/router.js - Hash Routing</h3>
188
+ <p>URL hash parsing and resolution.</p>
189
+ <h4 id="functions-4">Functions</h4>
190
+ <p><strong>`resolveTarget(hash: string): string`</strong></p>
191
+ <p>Extract section ID from URL hash.</p>
192
+ <pre><code class="language-javascript">resolveTarget(&#39;#/guides/setup&#39;); // &#39;guides/setup&#39;
193
+ resolveTarget(&#39;#&#39;); // &#39;&#39; (empty)</code></pre>
194
+ <p><strong>`resolveEntry(entry: SectionEntry): SectionEntry`</strong></p>
195
+ <p>Resolve a section entry, following redirects if needed.</p>
196
+ <hr>
197
+ <h3 id="libexportjs-document-export">lib/export.js - Document Export</h3>
198
+ <p>Compose sections into exportable HTML documents.</p>
199
+ <h4 id="functions-5">Functions</h4>
200
+ <p><strong>`composeExportDocument(chapters: Chapter[]): string`</strong></p>
201
+ <p>Generate complete HTML document from chapters.</p>
202
+ <pre><code class="language-javascript">const html = composeExportDocument([
203
+ { section: { title: &#39;Welcome&#39; }, html: &#39;&lt;p&gt;Hello&lt;/p&gt;&#39; },
204
+ { section: { title: &#39;Setup&#39; }, html: &#39;&lt;p&gt;Install...&lt;/p&gt;&#39; }
205
+ ]);
206
+ // Returns complete HTML with TOC, styles, syntax highlighting</code></pre>
207
+ <p><strong>`collectExportableSections(manifest: SectionEntry[]): SectionEntry[]`</strong></p>
208
+ <p>Get all sections that can be exported (have module paths).</p>
209
+ <pre><code class="language-javascript">const sections = collectExportableSections(MANIFEST);</code></pre>
210
+ <hr>
211
+ <h2 id="enhancement-modules">Enhancement Modules</h2>
212
+ <h3 id="mermaid-initjs-diagram-rendering">mermaid-init.js - Diagram Rendering</h3>
213
+ <p>Lazy-load and render Mermaid diagrams.</p>
214
+ <h4 id="functions-6">Functions</h4>
215
+ <p><strong>`renderMermaidBlocks(container: Element): Promise&lt;void&gt;`</strong></p>
216
+ <p>Find and render all Mermaid code blocks in a container.</p>
217
+ <pre><code class="language-javascript">await renderMermaidBlocks(document.querySelector(&#39;.canvas&#39;));
218
+ // Replaces ```mermaid blocks with rendered SVGs</code></pre>
219
+ <hr>
220
+ <h3 id="syntax-highlightjs-code-highlighting">syntax-highlight.js - Code Highlighting</h3>
221
+ <p>Lazy-load and apply Prism.js syntax highlighting.</p>
222
+ <h4 id="functions-7">Functions</h4>
223
+ <p><strong>`highlightCodeBlocks(container: Element): Promise&lt;void&gt;`</strong></p>
224
+ <p>Highlight all code blocks in a container.</p>
225
+ <pre><code class="language-javascript">await highlightCodeBlocks(document.querySelector(&#39;.canvas&#39;));
226
+ // Applies syntax highlighting to all &lt;code&gt; elements</code></pre>
227
+ <p>Supported languages: JavaScript, TypeScript, Python, Rust, Go, C, JSON, YAML, Bash, SQL, Solidity.</p>
228
+ <hr>
229
+ <h2 id="section-module-contract">Section Module Contract</h2>
230
+ <p>All section modules must export a `load` function:</p>
231
+ <pre><code class="language-javascript">/**
232
+ * Load section content.
233
+ * @returns {Promise&lt;{ html: string, afterRender?: (container: Element) =&gt; void }&gt;}
234
+ */
235
+ export async function load() {
236
+ return {
237
+ html: &#39;&lt;section class=&quot;section doc&quot;&gt;...&lt;/section&gt;&#39;,
238
+
239
+ // Optional: called after HTML is inserted into DOM
240
+ afterRender(container) {
241
+ // DOM manipulation, event listeners, etc.
242
+ }
243
+ };
244
+ }</code></pre>
245
+ <h3 id="examples">Examples</h3>
246
+ <p><strong>Static Content:</strong></p>
247
+ <pre><code class="language-javascript">export async function load() {
248
+ return {
249
+ html: `
250
+ &lt;section class=&quot;section doc markdown&quot;&gt;
251
+ &lt;div class=&quot;doc-content&quot;&gt;
252
+ &lt;h1&gt;Welcome&lt;/h1&gt;
253
+ &lt;p&gt;Hello, world!&lt;/p&gt;
254
+ &lt;/div&gt;
255
+ &lt;/section&gt;
256
+ `
257
+ };
258
+ }</code></pre>
259
+ <p><strong>Dynamic Content:</strong></p>
260
+ <pre><code class="language-javascript">export async function load() {
261
+ const data = await fetch(&#39;/api/stats.json&#39;).then(r =&gt; r.json());
262
+
263
+ return {
264
+ html: `
265
+ &lt;section class=&quot;section doc&quot;&gt;
266
+ &lt;h1&gt;Stats&lt;/h1&gt;
267
+ &lt;p&gt;Count: ${data.count}&lt;/p&gt;
268
+ &lt;/section&gt;
269
+ `,
270
+ afterRender(container) {
271
+ container.querySelector(&#39;button&#39;)?.addEventListener(&#39;click&#39;, refresh);
272
+ }
273
+ };
274
+ }</code></pre>
275
+ <hr>
276
+ <h2 id="type-definitions">Type Definitions</h2>
277
+ <pre><code class="language-typescript">interface SectionEntry {
278
+ id: string;
279
+ title: string;
280
+ summary?: string;
281
+ module?: string;
282
+ subsections?: SectionEntry[];
283
+ exclude?: boolean;
284
+ }
285
+
286
+ interface FlatSection extends SectionEntry {
287
+ group?: string; // Parent group title
288
+ }
289
+
290
+ interface IndexedSection extends FlatSection {
291
+ searchContent: string; // Lowercase text for searching
292
+ }
293
+
294
+ interface SiteConfig {
295
+ title: string;
296
+ description?: string;
297
+ brandMark?: string;
298
+ brandSub?: string;
299
+ tagline?: string;
300
+ copyright?: string;
301
+ }
302
+
303
+ interface Chapter {
304
+ section: { title: string; summary?: string };
305
+ html: string;
306
+ }</code></pre>
307
+ <hr>
308
+ <h2 id="build-scripts">Build Scripts</h2>
309
+ <h3 id="scriptsbuildjs">scripts/build.js</h3>
310
+ <p>Core build script for copying and minifying assets.</p>
311
+ <pre><code class="language-bash">node scripts/build.js [--dev]</code></pre>
312
+ <p>Options:</p>
313
+ <ul>
314
+ <li>`--dev` - Skip minification</li>
315
+ </ul>
316
+ <h3 id="scriptsbuild-tenantsjs">scripts/build-tenants.js</h3>
317
+ <p>Multi-tenant build orchestrator.</p>
318
+ <pre><code class="language-bash">node scripts/build-tenants.js [tenant-id] [--incremental]</code></pre>
319
+ <p>Arguments:</p>
320
+ <ul>
321
+ <li>`tenant-id` - Build specific tenant (omit for all)</li>
322
+ <li>`--incremental` - Only rebuild changed files</li>
323
+ </ul>
324
+ <h3 id="scriptsservejs">scripts/serve.js</h3>
325
+ <p>Development server.</p>
326
+ <pre><code class="language-bash">node scripts/serve.js [--port=5173]</code></pre>
327
+ <h3 id="scriptssync-docsjs">scripts/sync-docs.js</h3>
328
+ <p>Regenerate section template modules.</p>
329
+ <pre><code class="language-bash">node scripts/sync-docs.js</code></pre>
330
+ </div>
331
+ </section>
332
+ </div>
333
+ </article>
334
+ <footer class="static-footer">
335
+ <p>View interactive version: <a href="/#api">API Reference</a></p>
336
+ </footer>
337
+ </main>
338
+ </body>
339
+ </html>
@@ -0,0 +1,303 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Architecture | Pagenary Docs</title>
7
+ <meta name="description" content="The static SPA pattern, build pipeline, and tenant content model." />
8
+ <link rel="canonical" href="/#architecture" />
9
+
10
+ <!-- Open Graph -->
11
+ <meta property="og:title" content="Architecture" />
12
+ <meta property="og:description" content="The static SPA pattern, build pipeline, and tenant content model." />
13
+ <meta property="og:type" content="article" />
14
+ <meta property="og:url" content="/#architecture" />
15
+
16
+ <!-- Twitter Card -->
17
+ <meta name="twitter:card" content="summary" />
18
+ <meta name="twitter:title" content="Architecture" />
19
+ <meta name="twitter:description" content="The static SPA pattern, build pipeline, and tenant content model." />
20
+
21
+ <!-- Structured Data -->
22
+ <script type="application/ld+json">
23
+ [
24
+ {
25
+ "@context": "https://schema.org",
26
+ "@type": "TechArticle",
27
+ "headline": "Architecture",
28
+ "description": "The static SPA pattern, build pipeline, and tenant content model.",
29
+ "url": "/#architecture",
30
+ "dateModified": "2026-05-26",
31
+ "mainEntityOfPage": {
32
+ "@type": "WebPage",
33
+ "@id": "/#architecture"
34
+ },
35
+ "isPartOf": {
36
+ "@type": "WebSite",
37
+ "name": "Pagenary Docs",
38
+ "url": "/"
39
+ }
40
+ },
41
+ {
42
+ "@context": "https://schema.org",
43
+ "@type": "BreadcrumbList",
44
+ "itemListElement": [
45
+ {
46
+ "@type": "ListItem",
47
+ "position": 1,
48
+ "name": "Home",
49
+ "item": "/"
50
+ },
51
+ {
52
+ "@type": "ListItem",
53
+ "position": 2,
54
+ "name": "Reference",
55
+ "item": "/#reference"
56
+ },
57
+ {
58
+ "@type": "ListItem",
59
+ "position": 3,
60
+ "name": "Architecture",
61
+ "item": "/#architecture"
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ </script>
67
+
68
+ <!-- Redirect to SPA for JavaScript-enabled browsers -->
69
+ <script>
70
+ if (typeof window !== 'undefined') {
71
+ window.location.replace('/#architecture');
72
+ }
73
+ </script>
74
+ <noscript>
75
+ <meta http-equiv="refresh" content="0; url=/#architecture" />
76
+ </noscript>
77
+
78
+ <link rel="stylesheet" href="../styles.css" />
79
+ <style>
80
+ .static-content { max-width: 800px; margin: 0 auto; padding: 2rem; }
81
+ .static-footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid #eee; font-size: 0.9rem; color: #666; }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <main class="static-content">
86
+ <article>
87
+ <h1>Architecture</h1>
88
+ <p class="lead">The static SPA pattern, build pipeline, and tenant content model.</p>
89
+ <div class="section-body">
90
+ <section class="section doc markdown">
91
+ <div class="doc-content">
92
+ <h1 id="pagenary-architecture">Pagenary Architecture</h1>
93
+ <p>A minimalist multi-tenant documentation platform built around static assets and client-side rendering.</p>
94
+ <h2 id="design-principles">Design Principles</h2>
95
+ <ul>
96
+ <li><strong>Zero Runtime Dependencies</strong> - Vanilla HTML, CSS, and ES modules keep the footprint tiny</li>
97
+ <li><strong>Static-First</strong> - Hash-based routing (`#/page-id`) works on any static host</li>
98
+ <li><strong>Multi-Tenant Isolation</strong> - Each tenant gets isolated content, branding, and configuration</li>
99
+ <li><strong>Progressive Enhancement</strong> - Core content works without JavaScript; features enhance with it</li>
100
+ </ul>
101
+ <h2 id="system-overview">System Overview</h2>
102
+ <pre><code>┌─────────────────────────────────────────────────────────────┐
103
+ │ Build System │
104
+ │ ┌──────────┐ ┌──────────────┐ ┌───────────────────────┐ │
105
+ │ │ Tenant │ │ Content │ │ Asset Pipeline │ │
106
+ │ │ Registry │──│ Processor │──│ (Minify, Copy, Brand) │ │
107
+ │ └──────────┘ └──────────────┘ └───────────────────────┘ │
108
+ └─────────────────────────────────────────────────────────────┘
109
+
110
+
111
+ ┌─────────────────────────────────────────────────────────────┐
112
+ │ Static Bundle (dist/) │
113
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
114
+ │ │index.html│ │ app.js │ │styles.css│ │ sections/ │ │
115
+ │ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
116
+ └─────────────────────────────────────────────────────────────┘
117
+
118
+
119
+ ┌─────────────────────────────────────────────────────────────┐
120
+ │ Runtime (Browser) │
121
+ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
122
+ │ │ Router │ │ Search │ │ Renderer │ │ Export │ │
123
+ │ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
124
+ └─────────────────────────────────────────────────────────────┘</code></pre>
125
+ <h2 id="build-system">Build System</h2>
126
+ <h3 id="tenant-registry">Tenant Registry</h3>
127
+ <p>`tenants.json` maps tenant IDs to content sources:</p>
128
+ <pre><code class="language-json">{
129
+ &quot;tenant-id&quot;: {
130
+ &quot;source&quot;: &quot;/path/or/git:url#branch&quot;,
131
+ &quot;domain&quot;: &quot;docs.example.com&quot;
132
+ }
133
+ }</code></pre>
134
+ <p>Supports local paths and git repositories. Git sources are cloned to a cache directory.</p>
135
+ <h3 id="content-processor">Content Processor</h3>
136
+ <p>Transforms source content into section modules:</p>
137
+ <table><thead><tr><th style="text-align: left">Input</th><th style="text-align: left">Processing</th><th style="text-align: left">Output</th></tr></thead><tbody><tr><td style="text-align: left">`.md`</td><td style="text-align: left">Parse Markdown → HTML</td><td style="text-align: left">ES module with `load()`</td></tr><tr><td style="text-align: left">`.html`</td><td style="text-align: left">Wrap in loader</td><td style="text-align: left">ES module with `load()`</td></tr><tr><td style="text-align: left">`.js`</td><td style="text-align: left">Copy unchanged</td><td style="text-align: left">ES module with `load()`</td></tr></tbody></table>
138
+ <h3 id="asset-pipeline">Asset Pipeline</h3>
139
+ <p>1. <strong>Copy</strong> - Static assets from `src/` to `dist/`</p>
140
+ <p>2. <strong>Minify</strong> - JavaScript via Terser (production)</p>
141
+ <p>3. <strong>Brand</strong> - Apply tenant config (colors, text)</p>
142
+ <p>4. <strong>Override</strong> - Replace files from `overrides/`</p>
143
+ <h2 id="runtime-architecture">Runtime Architecture</h2>
144
+ <h3 id="shell-layout">Shell Layout</h3>
145
+ <pre><code>┌─────────────────────────────────────────────────────────┐
146
+ │ Top Bar: Menu Toggle │ Brand │ Command Palette │ Export │
147
+ ├───────────────┬─────────────────────────────────────────┤
148
+ │ │ │
149
+ │ Sidebar │ Canvas │
150
+ │ (Nav) │ (Content) │
151
+ │ │ │
152
+ ├───────────────┴─────────────────────────────────────────┤
153
+ │ Footer │
154
+ └─────────────────────────────────────────────────────────┘</code></pre>
155
+ <h3 id="module-structure">Module Structure</h3>
156
+ <pre><code>src/
157
+ ├── index.html # Shell template
158
+ ├── app.js # Core controller
159
+ ├── styles.css # All styling
160
+ ├── manifest.js # Navigation registry
161
+ ├── seo.js # Meta tag management
162
+ ├── mermaid-init.js # Diagram rendering
163
+ ├── syntax-highlight.js # Code highlighting
164
+ └── lib/
165
+ ├── search.js # Full-text search
166
+ ├── router.js # Hash routing utilities
167
+ └── export.js # Document export</code></pre>
168
+ <h3 id="core-flow">Core Flow</h3>
169
+ <pre><code>Hash Change → Router → Manifest Lookup → Module Import → Render → Post-Process
170
+
171
+ ├── Mermaid Diagrams
172
+ ├── Syntax Highlighting
173
+ └── SEO Meta Tags</code></pre>
174
+ <h2 id="key-components">Key Components</h2>
175
+ <h3 id="router-appjs">Router (app.js)</h3>
176
+ <p>Hash-based navigation with history support:</p>
177
+ <pre><code class="language-javascript">// URL: https://docs.example.com/#/guides/setup
178
+ // Resolves to section ID: &quot;guides/setup&quot;
179
+
180
+ window.addEventListener(&#39;hashchange&#39;, handleRoute);
181
+
182
+ function handleRoute() {
183
+ const id = resolveTarget(location.hash);
184
+ const section = findSection(id);
185
+ await loadSection(section);
186
+ }</code></pre>
187
+ <h3 id="search-libsearchjs">Search (lib/search.js)</h3>
188
+ <p>Full-text search with lazy indexing:</p>
189
+ <pre><code class="language-javascript">// First search: load all modules, extract text, build index
190
+ // Subsequent: search cached index
191
+
192
+ async function buildSearchIndex(manifest) {
193
+ const sections = flattenManifest(manifest);
194
+ return Promise.all(sections.map(async (section) =&gt; {
195
+ const mod = await import(section.module);
196
+ const { html } = await mod.load();
197
+ return { ...section, searchContent: extractText(html) };
198
+ }));
199
+ }</code></pre>
200
+ <h3 id="mermaid-integration-mermaid-initjs">Mermaid Integration (mermaid-init.js)</h3>
201
+ <p>Lazy-loaded diagram rendering:</p>
202
+ <pre><code class="language-javascript">export async function renderMermaidBlocks(container) {
203
+ const blocks = container.querySelectorAll(&#39;pre &gt; code.language-mermaid&#39;);
204
+ if (!blocks.length) return;
205
+
206
+ const mermaid = await import(&#39;https://esm.sh/mermaid@11&#39;);
207
+ mermaid.default.initialize({ startOnLoad: false });
208
+
209
+ for (const block of blocks) {
210
+ const { svg } = await mermaid.default.render(id, block.textContent);
211
+ // Replace code block with rendered SVG
212
+ }
213
+ }</code></pre>
214
+ <h3 id="syntax-highlighting-syntax-highlightjs">Syntax Highlighting (syntax-highlight.js)</h3>
215
+ <p>Prism.js integration with language auto-detection:</p>
216
+ <pre><code class="language-javascript">export async function highlightCodeBlocks(container) {
217
+ const blocks = container.querySelectorAll(&#39;pre &gt; code[class*=&quot;language-&quot;]&#39;);
218
+ if (!blocks.length) return;
219
+
220
+ const Prism = await import(&#39;https://esm.sh/prismjs@1.29.0&#39;);
221
+ // Load language modules dynamically
222
+ Prism.highlightAllUnder(container);
223
+ }</code></pre>
224
+ <h3 id="export-libexportjs">Export (lib/export.js)</h3>
225
+ <p>Document composition for print/PDF:</p>
226
+ <pre><code class="language-javascript">export function composeExportDocument(chapters) {
227
+ // Generate TOC
228
+ const toc = chapters.map((ch, i) =&gt; `&lt;li&gt;${i+1}. ${ch.title}&lt;/li&gt;`);
229
+
230
+ // Compose sections
231
+ const body = chapters.map((ch, i) =&gt; `
232
+ &lt;section&gt;
233
+ &lt;h2&gt;${i+1}. ${ch.title}&lt;/h2&gt;
234
+ ${ch.html}
235
+ &lt;/section&gt;
236
+ `);
237
+
238
+ return `&lt;!doctype html&gt;...${toc}...${body}...`;
239
+ }</code></pre>
240
+ <h2 id="multi-tenant-architecture">Multi-Tenant Architecture</h2>
241
+ <h3 id="build-time-isolation">Build-Time Isolation</h3>
242
+ <p>Each tenant build produces an isolated bundle:</p>
243
+ <pre><code>dist/
244
+ ├── tenant-a/
245
+ │ ├── index.html # Branded shell
246
+ │ ├── manifest.js # Tenant navigation
247
+ │ ├── styles.css # Themed styles
248
+ │ └── sections/ # Tenant content
249
+ └── tenant-b/
250
+ └── ... # Completely separate</code></pre>
251
+ <h3 id="runtime-isolation">Runtime Isolation</h3>
252
+ <ul>
253
+ <li>No shared state between tenants</li>
254
+ <li>Each tenant loads its own manifest</li>
255
+ <li>Theming via CSS variables replaced at build time</li>
256
+ </ul>
257
+ <h3 id="caddy-routing">Caddy Routing</h3>
258
+ <p>Multi-tenant domain routing via Caddy:</p>
259
+ <pre><code>tenant-a.example.com → dist/tenant-a/
260
+ tenant-b.example.com → dist/tenant-b/</code></pre>
261
+ <h2 id="performance-characteristics">Performance Characteristics</h2>
262
+ <h3 id="bundle-size">Bundle Size</h3>
263
+ <table><thead><tr><th style="text-align: left">Component</th><th style="text-align: left">Size (minified)</th></tr></thead><tbody><tr><td style="text-align: left">Shell (HTML/CSS/JS)</td><td style="text-align: left">~50 KB</td></tr><tr><td style="text-align: left">Per section</td><td style="text-align: left">~1-5 KB</td></tr><tr><td style="text-align: left">Mermaid (lazy)</td><td style="text-align: left">~800 KB</td></tr><tr><td style="text-align: left">Prism (lazy)</td><td style="text-align: left">~30 KB</td></tr></tbody></table>
264
+ <h3 id="loading-strategy">Loading Strategy</h3>
265
+ <p>1. <strong>Critical Path</strong> - Shell + manifest + first section</p>
266
+ <p>2. <strong>Lazy Load</strong> - Other sections on navigation</p>
267
+ <p>3. <strong>On-Demand</strong> - Mermaid/Prism when needed</p>
268
+ <p>4. <strong>Cached</strong> - Search index after first search</p>
269
+ <h2 id="extensibility-points">Extensibility Points</h2>
270
+ <h3 id="custom-page-types">Custom Page Types</h3>
271
+ <p>Add to `section-templates.js`:</p>
272
+ <pre><code class="language-javascript">export const templates = {
273
+ &#39;custom-type&#39;: {
274
+ render: (data) =&gt; `&lt;section class=&quot;custom&quot;&gt;...&lt;/section&gt;`
275
+ }
276
+ };</code></pre>
277
+ <h3 id="custom-components">Custom Components</h3>
278
+ <p>Use HTML classes in content:</p>
279
+ <div class="html-block"><div class="my-component">...</div></div>
280
+ <p>Add styles to tenant&#39;s `overrides/styles.css`.</p>
281
+ <h3 id="dynamic-data">Dynamic Data</h3>
282
+ <p>JavaScript modules can fetch external data:</p>
283
+ <pre><code class="language-javascript">export async function load() {
284
+ const data = await fetch(&#39;/api/data.json&#39;).then(r =&gt; r.json());
285
+ return { html: renderWithData(data) };
286
+ }</code></pre>
287
+ <h2 id="security-considerations">Security Considerations</h2>
288
+ <ul>
289
+ <li><strong>No Server-Side Code</strong> - Pure static assets</li>
290
+ <li><strong>CSP Compatible</strong> - No inline scripts in content</li>
291
+ <li><strong>Sandboxed Content</strong> - Each tenant in separate directory</li>
292
+ <li><strong>No User Data</strong> - Only localStorage for UI state</li>
293
+ </ul>
294
+ </div>
295
+ </section>
296
+ </div>
297
+ </article>
298
+ <footer class="static-footer">
299
+ <p>View interactive version: <a href="/#architecture">Architecture</a></p>
300
+ </footer>
301
+ </main>
302
+ </body>
303
+ </html>