@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
package/README.md ADDED
@@ -0,0 +1,337 @@
1
+ # Pagenary Publisher
2
+
3
+ Static publishing component for Pagenary — "Where documentation takes shape."
4
+
5
+ Transform shared documentation templates into tenant-specific bundles with custom branding, themes, and content. Zero runtime dependencies, hash-based routing, and full-text search make it ideal for white-label documentation portals.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ npm install
11
+ npm run dev # Build + serve with watch mode
12
+
13
+ # Or separately:
14
+ npm run build # Build default bundle to dist/
15
+ npm run serve # Preview on http://localhost:5173
16
+ ```
17
+
18
+ ## Features
19
+
20
+ ### Content Authoring
21
+ - **Markdown** - Write in `.md` files with full CommonMark support
22
+ - **HTML** - Direct markup control with `.html` files
23
+ - **JavaScript Modules** - Dynamic content with `.js` files returning `{ html, afterRender? }`
24
+ - **Nested Directories** - Organize content in subdirectories (`content/guides/setup.md`)
25
+
26
+ ### Rich Content
27
+ - **Mermaid Diagrams** - Flowcharts, sequence diagrams, state machines, and more
28
+ - **Syntax Highlighting** - Prism.js with 10+ language support
29
+ - **Markdown Tables** - Full table syntax with alignment support
30
+ - **HTML Components** - Spec tables, layer stacks, box diagrams, cards
31
+ - **Internal Links** - Auto-resolved `#section-id` links in Markdown
32
+
33
+ ### External Links
34
+ - **Navigation Links** - Add external URLs directly in manifest with `url` property
35
+ - **Smart Link Handling** - All external links open in new tab with security headers
36
+ - **Visual Indicators** - Subtle ↗ icon shows external destinations
37
+ - **CTA Styling** - Button-like `external-cta` class for prominent external links
38
+
39
+ **External navigation example** (manifest.json):
40
+ ```json
41
+ [
42
+ { "id": "welcome", "title": "Welcome", "file": "welcome.md" },
43
+ { "title": "External Resource", "url": "https://example.com" }
44
+ ]
45
+ ```
46
+
47
+ **External links in Markdown** (auto-handled):
48
+ ```markdown
49
+ Visit our [support portal](https://support.example.com) for help.
50
+ ```
51
+
52
+ **Prominent CTA in HTML**:
53
+ ```html
54
+ <a href="https://example.com" target="_blank" rel="noopener noreferrer" class="external-cta">
55
+ Get Started →
56
+ </a>
57
+ ```
58
+
59
+ **Security & UX:**
60
+ - All external links use `target="_blank"` and `rel="noopener noreferrer"` by default
61
+ - Navigation external links show ↗ indicator
62
+ - Content external links styled with subtle ↗ after link text
63
+ - No configuration needed - works automatically for `http://` and `https://` URLs
64
+
65
+ ### Navigation & Search
66
+ - **Command Palette** - `Ctrl/Cmd+K` or `/` opens global finder
67
+ - **Full-Text Search** - Searches all content, not just titles
68
+ - **Manifest-Driven Nav** - Declarative navigation structure
69
+ - **Keyboard Navigation** - Arrow keys, Enter to select
70
+
71
+ ### Theming & Branding
72
+ - **Custom Colors** - `accentColor` and `surfaceColor` per tenant
73
+ - **Brand Identity** - Logo text, tagline, copyright
74
+ - **Typography** - IBM Plex Sans/Mono defaults, customizable
75
+
76
+ ### Export & Sharing
77
+ - **Export Options** - Choose between Current Page or Entire Site export
78
+ - **Branded Exports** - Tenant logo, brand name, and tagline in export header
79
+ - **Document Export** - One-click HTML export with TOC
80
+ - **Print Styles** - Optimized for PDF generation
81
+ - **Syntax Highlighting** - Preserved in exports
82
+ - **Table Rendering** - Markdown tables render correctly in exports
83
+
84
+ ## Tenant Content Workflow
85
+
86
+ ### Directory Structure
87
+
88
+ ```
89
+ my-tenant/
90
+ ├── config.json # Branding and theme settings
91
+ ├── manifest.json # Navigation structure (optional)
92
+ ├── content/ # Content files
93
+ │ ├── welcome.md # Root-level content
94
+ │ ├── guides/ # Nested directory
95
+ │ │ ├── _manifest.json # Section manifest
96
+ │ │ ├── getting-started.md
97
+ │ │ └── advanced.md
98
+ │ └── api/
99
+ │ ├── _manifest.json
100
+ │ └── reference.md
101
+ └── overrides/ # Post-build file replacements (optional)
102
+ ```
103
+
104
+ ### Content Types
105
+
106
+ **Markdown (.md)**
107
+ ```markdown
108
+ # Getting Started
109
+
110
+ Welcome to the docs. Here's a code example:
111
+
112
+ \`\`\`javascript
113
+ console.log('Hello, Pagenary!');
114
+ \`\`\`
115
+
116
+ And a Mermaid diagram:
117
+
118
+ \`\`\`mermaid
119
+ graph LR
120
+ A[Start] --> B[Build]
121
+ B --> C[Deploy]
122
+ \`\`\`
123
+ ```
124
+
125
+ **HTML (.html)**
126
+ ```html
127
+ <section class="section doc">
128
+ <h1>Custom HTML Section</h1>
129
+ <table class="spec-table">
130
+ <tr><th>Feature</th><th>Status</th></tr>
131
+ <tr><td>Search</td><td>Ready</td></tr>
132
+ </table>
133
+ </section>
134
+ ```
135
+
136
+ **JavaScript (.js)**
137
+ ```javascript
138
+ export async function load() {
139
+ const data = await fetch('/api/metrics.json').then(r => r.json());
140
+ return {
141
+ html: `<section><h1>Metrics: ${data.count}</h1></section>`,
142
+ afterRender(container) {
143
+ // DOM manipulation after render
144
+ }
145
+ };
146
+ }
147
+ ```
148
+
149
+ ### Manifest Configuration
150
+
151
+ **Root manifest.json** (optional - auto-generated from content/ if omitted):
152
+ ```json
153
+ [
154
+ {
155
+ "id": "welcome",
156
+ "title": "Welcome",
157
+ "file": "welcome.md"
158
+ },
159
+ {
160
+ "id": "guides",
161
+ "title": "Guides",
162
+ "subsections": [
163
+ { "id": "guides/getting-started", "title": "Getting Started", "file": "guides/getting-started.md" },
164
+ { "id": "guides/advanced", "title": "Advanced Usage", "file": "guides/advanced.md" }
165
+ ]
166
+ }
167
+ ]
168
+ ```
169
+
170
+ **Section _manifest.json** (in content subdirectories):
171
+ ```json
172
+ {
173
+ "title": "API Reference",
174
+ "sections": [
175
+ { "id": "overview", "title": "Overview", "file": "overview.md" },
176
+ { "id": "endpoints", "title": "Endpoints", "file": "endpoints.md" }
177
+ ]
178
+ }
179
+ ```
180
+
181
+ **External links in manifest** (use `url` instead of `id`):
182
+ ```json
183
+ [
184
+ { "id": "welcome", "title": "Welcome", "file": "welcome.md" },
185
+ { "title": "Support Portal", "url": "https://support.example.com" },
186
+ {
187
+ "id": "resources",
188
+ "title": "Resources",
189
+ "subsections": [
190
+ { "id": "guides/overview", "title": "Overview", "file": "guides/overview.md" },
191
+ { "title": "API Docs", "url": "https://api.example.com/docs" }
192
+ ]
193
+ }
194
+ ]
195
+ ```
196
+
197
+ ### Branding Configuration
198
+
199
+ **config.json**:
200
+ ```json
201
+ {
202
+ "title": "My Documentation",
203
+ "description": "Comprehensive guide to our platform",
204
+ "brandMark": "ACME",
205
+ "brandSub": "Docs",
206
+ "tagline": "Build better, faster",
207
+ "copyright": "ACME Corp",
208
+ "accentColor": "#6366F1",
209
+ "surfaceColor": "#F7FAFC",
210
+ "export": {
211
+ "logo": "embed",
212
+ "logoPath": "favicon.png",
213
+ "showTagline": true,
214
+ "showDate": true
215
+ }
216
+ }
217
+ ```
218
+
219
+ | Property | Description | Default |
220
+ |----------|-------------|---------|
221
+ | `title` | Browser tab title | "Docs Toolkit" |
222
+ | `description` | Meta description for SEO | - |
223
+ | `brandMark` | Primary brand text (bold) | "DOCS" |
224
+ | `brandSub` | Secondary brand text (light) | "TOOLKIT" |
225
+ | `tagline` | Subtitle under brand | - |
226
+ | `copyright` | Footer copyright text | "Modular Documentation Toolkit" |
227
+ | `accentColor` | Links, buttons, highlights | `#111111` |
228
+ | `surfaceColor` | Background color (hex) | `#ffffff` |
229
+ | `export.logo` | Logo mode: `"embed"`, `"reference"`, or `null` | `"embed"` |
230
+ | `export.logoPath` | Path to logo in `.public/` directory | Auto-detect |
231
+ | `export.showTagline` | Show tagline in export header | `true` |
232
+ | `export.showDate` | Show generation date in export | `true` |
233
+
234
+ ## Build Commands
235
+
236
+ ```bash
237
+ # Full builds
238
+ npm run build # Build default bundle
239
+ npm run build:tenants # Build all registered tenants
240
+ npm run build:tenants my-tenant # Build specific tenant
241
+
242
+ # Incremental builds (git-aware)
243
+ npm run build:incremental my-tenant # Only rebuild changed files
244
+
245
+ # Development
246
+ npm run dev # Build + serve with watch
247
+ npm run serve # Serve dist/ on localhost:5173
248
+
249
+ # Utilities
250
+ npm run lint:content # Check for trailing whitespace/tabs
251
+ npm run check:seo # Verify SEO metadata
252
+ npm run check # Run all checks
253
+ npm run sync:docs # Regenerate section templates
254
+ npm test # Run test suite
255
+ ```
256
+
257
+ ## Tenant Registry
258
+
259
+ Register tenants in `tenants.json`:
260
+
261
+ ```json
262
+ {
263
+ "my-docs": {
264
+ "source": "/absolute/path/to/my-docs",
265
+ "domain": "my-docs.local"
266
+ },
267
+ "client-portal": {
268
+ "source": "git:https://github.com/org/client-docs.git#main",
269
+ "domain": "docs.client.com"
270
+ }
271
+ }
272
+ ```
273
+
274
+ **Source types:**
275
+ - **Local path**: `/absolute/path/to/content`
276
+ - **Git repository**: `git:https://github.com/org/repo.git#branch`
277
+
278
+ ## Docker Caddy Workflow
279
+
280
+ For multi-tenant domain testing:
281
+
282
+ ```bash
283
+ # Add to /etc/hosts:
284
+ # 127.0.0.1 my-docs.local client-portal.local
285
+
286
+ # Build tenants and start Caddy
287
+ npm run build:tenants
288
+ npm run caddy:up
289
+
290
+ # Visit http://my-docs.local or http://client-portal.local
291
+
292
+ # Management commands
293
+ npm run caddy:logs # Tail logs
294
+ npm run caddy:reload # Reload config without restart
295
+ npm run caddy:restart # Full restart
296
+ npm run caddy:down # Stop container
297
+ ```
298
+
299
+ Use non-privileged port: `DOCS_TOOLKIT_PORT=5173 npm run caddy:up`
300
+
301
+ ## Repository Layout
302
+
303
+ ```
304
+ apps/publisher/
305
+ ├── src/
306
+ │ ├── index.html # SPA shell
307
+ │ ├── app.js # Router and core logic
308
+ │ ├── styles.css # All styling
309
+ │ ├── manifest.js # Default navigation
310
+ │ ├── seo.js # Meta tag management
311
+ │ ├── mermaid-init.js # Diagram rendering
312
+ │ ├── syntax-highlight.js # Code highlighting
313
+ │ ├── lib/
314
+ │ │ ├── search.js # Full-text search
315
+ │ │ ├── router.js # Hash routing
316
+ │ │ └── export.js # Document export
317
+ │ └── sections/ # Default section modules
318
+ ├── scripts/
319
+ │ ├── build.js # Core build script
320
+ │ ├── build-tenants.js # Multi-tenant builder
321
+ │ ├── serve.js # Dev server
322
+ │ └── sync-docs.js # Template sync
323
+ ├── tenants/ # Built-in example tenants
324
+ ├── docs/ # Documentation
325
+ ├── dist/ # Build output
326
+ ├── Caddyfile # Multi-tenant routing
327
+ └── docker-compose.yml # Caddy container
328
+ ```
329
+
330
+ ## Documentation
331
+
332
+ - [Quick Start Guide](docs/QUICKSTART.md) - Step-by-step tenant creation
333
+ - [Tenant Configuration](docs/TENANT-CONFIG.md) - All config options
334
+ - [Architecture](docs/ARCHITECTURE.md) - System design
335
+ - [API Reference](docs/API.md) - Module documentation
336
+ - [Deployment](docs/DEPLOYMENT.md) - Hosting patterns
337
+ - [Extending](docs/EXTENDING.md) - Customization guide
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * pagenary — CLI entry point for @pagenary/publisher.
4
+ *
5
+ * A thin wrapper over the existing scripts/*.js ESM entry points so the
6
+ * package works as `npx @pagenary/publisher <command>` and as an installed
7
+ * devDependency CLI (`pagenary <command>`). Scripts are resolved relative to
8
+ * this file (the installed package), never the caller's CWD, so the right
9
+ * generator code runs regardless of where the command is invoked.
10
+ */
11
+
12
+ import { spawn } from 'node:child_process';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+ const scriptsDir = path.join(__dirname, '..', 'scripts');
18
+
19
+ // command -> { script, baseArgs, summary }
20
+ const COMMANDS = {
21
+ build: {
22
+ script: 'build.js',
23
+ baseArgs: [],
24
+ summary: 'Build the default bundle to dist/.'
25
+ },
26
+ 'build:tenants': {
27
+ script: 'build-tenants.js',
28
+ baseArgs: [],
29
+ summary: 'Build tenant bundles to dist/<id>/. Pass a tenant id to build one.'
30
+ },
31
+ 'tenants:list': {
32
+ script: 'build-tenants.js',
33
+ baseArgs: ['--list'],
34
+ summary: 'List configured tenants from the registry.'
35
+ },
36
+ serve: {
37
+ script: 'serve.js',
38
+ baseArgs: [],
39
+ summary: 'Serve the built output over HTTP.'
40
+ }
41
+ };
42
+
43
+ // Convenience aliases.
44
+ const ALIASES = {
45
+ 'build:tenant': 'build:tenants'
46
+ };
47
+
48
+ function printHelp() {
49
+ const lines = [
50
+ 'pagenary — multi-tenant documentation publisher',
51
+ '',
52
+ 'Usage:',
53
+ ' pagenary <command> [options]',
54
+ ' npx @pagenary/publisher <command> [options]',
55
+ '',
56
+ 'Commands:'
57
+ ];
58
+ const width = Math.max(...Object.keys(COMMANDS).map((c) => c.length));
59
+ for (const [name, def] of Object.entries(COMMANDS)) {
60
+ lines.push(` ${name.padEnd(width)} ${def.summary}`);
61
+ }
62
+ lines.push(
63
+ '',
64
+ 'Examples:',
65
+ ' pagenary build',
66
+ ' pagenary build:tenants # build all enabled tenants',
67
+ ' pagenary build:tenants pagenary # build one tenant',
68
+ ' pagenary tenants:list',
69
+ ' pagenary serve',
70
+ '',
71
+ 'Any extra options are passed through to the underlying script, e.g.',
72
+ ' pagenary build:tenants --incremental',
73
+ ''
74
+ );
75
+ console.log(lines.join('\n'));
76
+ }
77
+
78
+ function main() {
79
+ const [, , rawCommand, ...rest] = process.argv;
80
+
81
+ if (!rawCommand || rawCommand === '--help' || rawCommand === '-h' || rawCommand === 'help') {
82
+ printHelp();
83
+ process.exit(0);
84
+ }
85
+
86
+ const command = ALIASES[rawCommand] || rawCommand;
87
+ const def = COMMANDS[command];
88
+
89
+ if (!def) {
90
+ console.error(`pagenary: unknown command "${rawCommand}"\n`);
91
+ printHelp();
92
+ process.exit(1);
93
+ }
94
+
95
+ const scriptPath = path.join(scriptsDir, def.script);
96
+ const args = [scriptPath, ...def.baseArgs, ...rest];
97
+
98
+ const child = spawn(process.execPath, args, {
99
+ stdio: 'inherit',
100
+ cwd: process.cwd()
101
+ });
102
+
103
+ child.on('error', (err) => {
104
+ console.error(`pagenary: failed to run ${command}: ${err.message}`);
105
+ process.exit(1);
106
+ });
107
+ child.on('exit', (code, signal) => {
108
+ if (signal) {
109
+ process.kill(process.pid, signal);
110
+ } else {
111
+ process.exit(code ?? 0);
112
+ }
113
+ });
114
+ }
115
+
116
+ main();
@@ -0,0 +1,5 @@
1
+ {
2
+ "minify": true,
3
+ "mangleReserved": ["load", "init"],
4
+ "sectionsMangleReserved": ["load"]
5
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@pagenary/publisher",
3
+ "version": "2026.5.0",
4
+ "type": "module",
5
+ "description": "Multi-tenant static publishing component for Pagenary platform.",
6
+ "license": "AGPL-3.0-or-later",
7
+ "homepage": "https://git.integrolabs.net/roctinam/pagenary",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/jmagly/pagenary.git",
11
+ "directory": "apps/publisher"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "bin": {
17
+ "pagenary": "bin/pagenary.mjs"
18
+ },
19
+ "files": [
20
+ "src/",
21
+ "scripts/",
22
+ "site/",
23
+ "build.config.json",
24
+ "tenants.schema.json",
25
+ "tenants.json.example",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "scripts": {
30
+ "dev": "npm run build:dev && npm run serve:dev",
31
+ "build": "node scripts/build.js",
32
+ "build:dev": "NODE_ENV=development node scripts/build.js --dev",
33
+ "build:tenants": "node scripts/build-tenants.js",
34
+ "build:tenant": "node scripts/build-tenants.js",
35
+ "build:site": "node scripts/build-tenants.js pagenary && node scripts/build-site.js",
36
+ "prepack": "node scripts/build-tenants.js pagenary >&2 && node scripts/build-site.js >&2",
37
+ "build:incremental": "node scripts/build-tenants.js --incremental",
38
+ "build:diff": "node scripts/build-tenants.js --diff-only",
39
+ "tenants:list": "node scripts/build-tenants.js --list",
40
+ "tenants:diff": "node scripts/build-tenants.js --diff-only",
41
+ "serve": "node scripts/serve.js",
42
+ "serve:dev": "NODE_ENV=development node scripts/serve.js --dev",
43
+ "start": "npm run build && npm run serve",
44
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
45
+ "sync:docs": "node scripts/generate-sections.js",
46
+ "lint:content": "node scripts/lint-content.js",
47
+ "check:seo": "node scripts/seo-smoke.js",
48
+ "check": "npm run lint:content && npm run build && npm run check:seo",
49
+ "test": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js",
50
+ "test:coverage": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --coverage",
51
+ "test:watch": "node --experimental-vm-modules ../../node_modules/jest/bin/jest.js --watch",
52
+ "ci": "npm run check && npm test",
53
+ "caddy:up": "docker compose up -d caddy",
54
+ "caddy:down": "docker compose down",
55
+ "caddy:restart": "docker compose restart caddy",
56
+ "caddy:logs": "docker compose logs -f caddy",
57
+ "caddy:reload": "docker compose exec caddy caddy reload --config /srv/app/Caddyfile"
58
+ },
59
+ "engines": {
60
+ "node": ">=16"
61
+ },
62
+ "devDependencies": {
63
+ "jest": "^29.7.0",
64
+ "terser": "^5.44.0"
65
+ }
66
+ }
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Flatten the compiled `pagenary` tenant bundle into a top-level `site/`
4
+ * directory so it ships inside the published @pagenary/publisher tarball
5
+ * (see issue #7). This runs from the `prepack` lifecycle hook, after
6
+ * `build:tenants pagenary` has produced `dist/pagenary/`.
7
+ *
8
+ * `prepack` fires for `npm pack` and `npm publish` but NOT for a consumer's
9
+ * `npm install` (that would be `prepare`), so installing the package never
10
+ * triggers a site build.
11
+ *
12
+ * Output shape: site/index.html (+ assets) at the top level — not
13
+ * site/pagenary/ — matching the tarball-audit expectation.
14
+ */
15
+
16
+ import { promises as fsp } from 'node:fs';
17
+ import path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+ const root = path.join(__dirname, '..');
22
+ const srcDir = path.join(root, 'dist', 'pagenary');
23
+ const destDir = path.join(root, 'site');
24
+
25
+ async function pathExists(target) {
26
+ try {
27
+ await fsp.access(target);
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Recursively copy a directory. Follows symlinks (the pagenary tenant uses
36
+ * repo-relative symlinks for source content, but dist/ is already a fully
37
+ * materialized build, so this copy sees regular files).
38
+ * Returns { files, bytes }.
39
+ */
40
+ async function copyDir(from, to) {
41
+ await fsp.mkdir(to, { recursive: true });
42
+ const entries = await fsp.readdir(from, { withFileTypes: true });
43
+ let files = 0;
44
+ let bytes = 0;
45
+ for (const entry of entries) {
46
+ const fromPath = path.join(from, entry.name);
47
+ const toPath = path.join(to, entry.name);
48
+ if (entry.isDirectory()) {
49
+ const sub = await copyDir(fromPath, toPath);
50
+ files += sub.files;
51
+ bytes += sub.bytes;
52
+ } else {
53
+ await fsp.copyFile(fromPath, toPath);
54
+ const stat = await fsp.stat(toPath);
55
+ files += 1;
56
+ bytes += stat.size;
57
+ }
58
+ }
59
+ return { files, bytes };
60
+ }
61
+
62
+ async function main() {
63
+ if (!(await pathExists(srcDir))) {
64
+ console.error(
65
+ `build-site: ${path.relative(root, srcDir)} not found. ` +
66
+ `Run "npm run build:tenants pagenary" first (prepack does this automatically).`
67
+ );
68
+ process.exit(1);
69
+ }
70
+
71
+ // Deterministic: start from a clean site/.
72
+ await fsp.rm(destDir, { recursive: true, force: true });
73
+
74
+ const { files, bytes } = await copyDir(srcDir, destDir);
75
+ const mb = (bytes / (1024 * 1024)).toFixed(2);
76
+ console.log(`build-site: copied ${files} files (${mb} MB) -> ${path.relative(root, destDir)}/`);
77
+
78
+ if (!(await pathExists(path.join(destDir, 'index.html')))) {
79
+ console.error('build-site: site/index.html missing after copy — build output looks incomplete.');
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ main().catch((err) => {
85
+ console.error(`build-site: ${err.message}`);
86
+ process.exit(1);
87
+ });