@runcontext/site 0.1.1 → 0.2.1
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.
- package/LICENSE +21 -0
- package/dist/index.cjs +609 -495
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -13
- package/dist/index.d.ts +75 -13
- package/dist/index.mjs +608 -494
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -10
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generator.ts","../src/templates.ts","../src/search/build-index.ts"],"sourcesContent":["import { mkdir, writeFile, readFile } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport ejs from 'ejs';\nimport type { Manifest, ManifestConcept } from '@runcontext/core';\nimport {\n layoutTemplate,\n indexTemplate,\n conceptTemplate,\n productTemplate,\n policyTemplate,\n ownerTemplate,\n glossaryTemplate,\n searchTemplate,\n} from './templates.js';\nimport { buildSearchIndex } from './search/build-index.js';\n\nexport interface GenerateSiteOptions {\n manifest: Manifest;\n outputDir: string;\n title?: string;\n basePath?: string;\n}\n\n/**\n * Render an EJS content template wrapped in the layout template.\n */\nfunction renderPage(\n contentTemplate: string,\n data: Record<string, unknown>,\n pageTitle: string,\n manifest: Manifest,\n basePath: string,\n): string {\n const content = ejs.render(contentTemplate, { ...data, basePath });\n return ejs.render(layoutTemplate, {\n content,\n pageTitle,\n project: manifest.project,\n build: manifest.build,\n basePath,\n });\n}\n\n/**\n * Write a file, creating parent directories as needed.\n */\nasync function writeOutputFile(filePath: string, content: string): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Resolve the path to a bundled asset file (search.js, style.css).\n * These are source assets that ship with the package.\n */\nfunction resolveAssetPath(filename: string): string {\n const currentDir = dirname(fileURLToPath(import.meta.url));\n // In development (src/), assets are in src/assets/\n // In built output (dist/), we still try src path first, then fall back\n return join(currentDir, 'assets', filename);\n}\n\n/**\n * Generate a static documentation site from a ContextKit manifest.\n *\n * Creates HTML pages for all concepts, products, policies, owners,\n * a glossary page, search page, and index page. Also generates a\n * MiniSearch index for client-side search.\n */\nexport async function generateSite(options: GenerateSiteOptions): Promise<void> {\n const { manifest, outputDir, title, basePath = '' } = options;\n const projectData = {\n ...manifest.project,\n displayName: title ?? manifest.project.displayName,\n };\n const manifestWithTitle = { ...manifest, project: projectData };\n\n // Create output directories\n await mkdir(join(outputDir, 'concepts'), { recursive: true });\n await mkdir(join(outputDir, 'products'), { recursive: true });\n await mkdir(join(outputDir, 'policies'), { recursive: true });\n await mkdir(join(outputDir, 'owners'), { recursive: true });\n\n // Render index page\n const indexHtml = renderPage(\n indexTemplate,\n {\n project: manifestWithTitle.project,\n concepts: manifest.concepts,\n products: manifest.products,\n policies: manifest.policies,\n entities: manifest.entities,\n terms: manifest.terms,\n owners: manifest.owners,\n },\n 'Home',\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'index.html'), indexHtml);\n\n // Render concept pages\n for (const concept of manifest.concepts) {\n const html = renderPage(\n conceptTemplate,\n { concept },\n concept.id,\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'concepts', `${concept.id}.html`), html);\n }\n\n // Render product pages\n for (const product of manifest.products) {\n const relatedConcepts: ManifestConcept[] = manifest.concepts.filter(\n (c) => c.productId === product.id,\n );\n const html = renderPage(\n productTemplate,\n { product, relatedConcepts },\n product.id,\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'products', `${product.id}.html`), html);\n }\n\n // Render policy pages\n for (const policy of manifest.policies) {\n const html = renderPage(\n policyTemplate,\n { policy },\n policy.id,\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'policies', `${policy.id}.html`), html);\n }\n\n // Render owner pages\n for (const owner of manifest.owners) {\n const ownedNodes: Array<{ id: string; kind: string; href: string }> = [];\n\n for (const concept of manifest.concepts) {\n if (concept.owner === owner.id) {\n ownedNodes.push({\n id: concept.id,\n kind: 'concept',\n href: `${basePath}/concepts/${concept.id}.html`,\n });\n }\n }\n for (const product of manifest.products) {\n if (product.owner === owner.id) {\n ownedNodes.push({\n id: product.id,\n kind: 'product',\n href: `${basePath}/products/${product.id}.html`,\n });\n }\n }\n for (const policy of manifest.policies) {\n if (policy.owner === owner.id) {\n ownedNodes.push({\n id: policy.id,\n kind: 'policy',\n href: `${basePath}/policies/${policy.id}.html`,\n });\n }\n }\n for (const entity of manifest.entities) {\n if (entity.owner === owner.id) {\n ownedNodes.push({\n id: entity.id,\n kind: 'entity',\n href: `${basePath}/`,\n });\n }\n }\n for (const term of manifest.terms) {\n if (term.owner === owner.id) {\n ownedNodes.push({\n id: term.id,\n kind: 'term',\n href: `${basePath}/glossary.html`,\n });\n }\n }\n\n const html = renderPage(\n ownerTemplate,\n { owner, ownedNodes },\n owner.displayName,\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'owners', `${owner.id}.html`), html);\n }\n\n // Render glossary page\n const glossaryHtml = renderPage(\n glossaryTemplate,\n { terms: manifest.terms },\n 'Glossary',\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'glossary.html'), glossaryHtml);\n\n // Render search page\n const searchHtml = renderPage(\n searchTemplate,\n {},\n 'Search',\n manifestWithTitle,\n basePath,\n );\n await writeOutputFile(join(outputDir, 'search.html'), searchHtml);\n\n // Build and write search index\n const searchIndexJson = buildSearchIndex(manifest);\n await writeOutputFile(join(outputDir, 'search-index.json'), searchIndexJson);\n\n // Write client-side assets\n // Try to read from source assets directory; fall back to embedded content\n let searchJs: string;\n try {\n searchJs = await readFile(resolveAssetPath('search.js'), 'utf-8');\n } catch {\n // Fallback: minimal search script\n searchJs = '// Search functionality - load search-index.json\\n';\n }\n await writeOutputFile(join(outputDir, 'search.js'), searchJs);\n\n let styleCss: string;\n try {\n styleCss = await readFile(resolveAssetPath('style.css'), 'utf-8');\n } catch {\n styleCss = '/* ContextKit Site Styles */\\n';\n }\n await writeOutputFile(join(outputDir, 'style.css'), styleCss);\n}\n","/**\n * Embedded EJS template strings for static site generation.\n * Templates are embedded rather than loaded from .ejs files to avoid\n * runtime file resolution issues when the package is bundled by tsup.\n */\n\nexport const layoutTemplate = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title><%= pageTitle %> - <%= project.displayName %></title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n <link rel=\"stylesheet\" href=\"<%= basePath %>/style.css\">\n</head>\n<body class=\"bg-gray-50 text-gray-900 min-h-screen flex flex-col\">\n <nav class=\"bg-white border-b border-gray-200 px-6 py-3\">\n <div class=\"max-w-6xl mx-auto flex items-center gap-6\">\n <a href=\"<%= basePath %>/\" class=\"font-bold text-lg text-blue-600\"><%= project.displayName %></a>\n <div class=\"flex gap-4 text-sm\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a>\n <a href=\"<%= basePath %>/glossary.html\" class=\"hover:text-blue-600\">Glossary</a>\n <a href=\"<%= basePath %>/search.html\" class=\"hover:text-blue-600\">Search</a>\n </div>\n </div>\n </nav>\n <main class=\"flex-1 max-w-6xl mx-auto w-full px-6 py-8\">\n <%- content %>\n </main>\n <footer class=\"bg-white border-t border-gray-200 px-6 py-4 text-center text-sm text-gray-500\">\n Built with <a href=\"https://github.com/contextkit\" class=\"text-blue-600 hover:underline\">ContextKit</a> · <%= build.timestamp %>\n </footer>\n</body>\n</html>`;\n\nexport const indexTemplate = `<h1 class=\"text-3xl font-bold mb-2\"><%= project.displayName %></h1>\n<p class=\"text-gray-600 mb-6\">Version <%= project.version %></p>\n\n<div class=\"grid grid-cols-2 md:grid-cols-3 gap-4 mb-8\">\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-blue-600\"><%= concepts.length %></div>\n <div class=\"text-sm text-gray-500\">Concepts</div>\n </div>\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-green-600\"><%= products.length %></div>\n <div class=\"text-sm text-gray-500\">Products</div>\n </div>\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-purple-600\"><%= policies.length %></div>\n <div class=\"text-sm text-gray-500\">Policies</div>\n </div>\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-orange-600\"><%= entities.length %></div>\n <div class=\"text-sm text-gray-500\">Entities</div>\n </div>\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-teal-600\"><%= terms.length %></div>\n <div class=\"text-sm text-gray-500\">Terms</div>\n </div>\n <div class=\"bg-white rounded-lg shadow p-4 text-center\">\n <div class=\"text-2xl font-bold text-red-600\"><%= owners.length %></div>\n <div class=\"text-sm text-gray-500\">Owners</div>\n </div>\n</div>\n\n<% if (concepts.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Concepts</h2>\n<ul class=\"mb-6 space-y-1\">\n <% concepts.forEach(function(c) { %>\n <li><a href=\"<%= basePath %>/concepts/<%= c.id %>.html\" class=\"text-blue-600 hover:underline\"><%= c.id %></a>\n <% if (c.certified) { %><span class=\"ml-1 text-xs bg-green-100 text-green-800 px-1.5 py-0.5 rounded\">certified</span><% } %>\n </li>\n <% }); %>\n</ul>\n<% } %>\n\n<% if (products.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Products</h2>\n<ul class=\"mb-6 space-y-1\">\n <% products.forEach(function(p) { %>\n <li><a href=\"<%= basePath %>/products/<%= p.id %>.html\" class=\"text-blue-600 hover:underline\"><%= p.id %></a></li>\n <% }); %>\n</ul>\n<% } %>\n\n<% if (policies.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Policies</h2>\n<ul class=\"mb-6 space-y-1\">\n <% policies.forEach(function(p) { %>\n <li><a href=\"<%= basePath %>/policies/<%= p.id %>.html\" class=\"text-blue-600 hover:underline\"><%= p.id %></a></li>\n <% }); %>\n</ul>\n<% } %>\n\n<% if (owners.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Owners</h2>\n<ul class=\"mb-6 space-y-1\">\n <% owners.forEach(function(o) { %>\n <li><a href=\"<%= basePath %>/owners/<%= o.id %>.html\" class=\"text-blue-600 hover:underline\"><%= o.displayName %></a></li>\n <% }); %>\n</ul>\n<% } %>`;\n\nexport const conceptTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Concepts › <%= concept.id %>\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-2\"><%= concept.id %>\n <% if (concept.certified) { %><span class=\"ml-2 text-sm bg-green-100 text-green-800 px-2 py-0.5 rounded\">certified</span><% } %>\n</h1>\n\n<p class=\"text-lg text-gray-700 mb-6\"><%= concept.definition %></p>\n\n<div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mb-6\">\n <% if (concept.owner) { %>\n <div class=\"bg-white rounded-lg shadow p-4\">\n <div class=\"text-sm text-gray-500\">Owner</div>\n <a href=\"<%= basePath %>/owners/<%= concept.owner %>.html\" class=\"text-blue-600 hover:underline\"><%= concept.owner %></a>\n </div>\n <% } %>\n <% if (concept.productId) { %>\n <div class=\"bg-white rounded-lg shadow p-4\">\n <div class=\"text-sm text-gray-500\">Product</div>\n <a href=\"<%= basePath %>/products/<%= concept.productId %>.html\" class=\"text-blue-600 hover:underline\"><%= concept.productId %></a>\n </div>\n <% } %>\n</div>\n\n<% if (concept.tags && concept.tags.length > 0) { %>\n<div class=\"mb-4\">\n <span class=\"text-sm text-gray-500 mr-2\">Tags:</span>\n <% concept.tags.forEach(function(tag) { %>\n <span class=\"inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1\"><%= tag %></span>\n <% }); %>\n</div>\n<% } %>\n\n<% if (concept.dependsOn && concept.dependsOn.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Dependencies</h2>\n<ul class=\"mb-6 space-y-1\">\n <% concept.dependsOn.forEach(function(dep) { %>\n <li><a href=\"<%= basePath %>/concepts/<%= dep %>.html\" class=\"text-blue-600 hover:underline\"><%= dep %></a></li>\n <% }); %>\n</ul>\n<% } %>`;\n\nexport const productTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Products › <%= product.id %>\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-2\"><%= product.id %></h1>\n<p class=\"text-lg text-gray-700 mb-6\"><%= product.description %></p>\n\n<% if (product.owner) { %>\n<div class=\"mb-4\">\n <span class=\"text-sm text-gray-500 mr-2\">Owner:</span>\n <a href=\"<%= basePath %>/owners/<%= product.owner %>.html\" class=\"text-blue-600 hover:underline\"><%= product.owner %></a>\n</div>\n<% } %>\n\n<% if (product.tags && product.tags.length > 0) { %>\n<div class=\"mb-4\">\n <span class=\"text-sm text-gray-500 mr-2\">Tags:</span>\n <% product.tags.forEach(function(tag) { %>\n <span class=\"inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1\"><%= tag %></span>\n <% }); %>\n</div>\n<% } %>\n\n<% if (relatedConcepts.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Concepts</h2>\n<ul class=\"mb-6 space-y-1\">\n <% relatedConcepts.forEach(function(c) { %>\n <li><a href=\"<%= basePath %>/concepts/<%= c.id %>.html\" class=\"text-blue-600 hover:underline\"><%= c.id %></a> — <%= c.definition %></li>\n <% }); %>\n</ul>\n<% } %>`;\n\nexport const policyTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Policies › <%= policy.id %>\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-2\"><%= policy.id %></h1>\n<p class=\"text-lg text-gray-700 mb-6\"><%= policy.description %></p>\n\n<% if (policy.owner) { %>\n<div class=\"mb-4\">\n <span class=\"text-sm text-gray-500 mr-2\">Owner:</span>\n <a href=\"<%= basePath %>/owners/<%= policy.owner %>.html\" class=\"text-blue-600 hover:underline\"><%= policy.owner %></a>\n</div>\n<% } %>\n\n<% if (policy.tags && policy.tags.length > 0) { %>\n<div class=\"mb-4\">\n <span class=\"text-sm text-gray-500 mr-2\">Tags:</span>\n <% policy.tags.forEach(function(tag) { %>\n <span class=\"inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1\"><%= tag %></span>\n <% }); %>\n</div>\n<% } %>\n\n<% if (policy.rules && policy.rules.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Rules</h2>\n<div class=\"overflow-x-auto\">\n <table class=\"min-w-full bg-white rounded-lg shadow text-sm\">\n <thead>\n <tr class=\"bg-gray-50 text-left\">\n <th class=\"px-4 py-2\">Priority</th>\n <th class=\"px-4 py-2\">When</th>\n <th class=\"px-4 py-2\">Then</th>\n </tr>\n </thead>\n <tbody>\n <% policy.rules.forEach(function(rule) { %>\n <tr class=\"border-t\">\n <td class=\"px-4 py-2\"><%= rule.priority %></td>\n <td class=\"px-4 py-2\">\n <% if (rule.when.tagsAny) { %>tags: <%= rule.when.tagsAny.join(', ') %><% } %>\n <% if (rule.when.conceptIds) { %>concepts: <%= rule.when.conceptIds.join(', ') %><% } %>\n <% if (rule.when.status) { %>status: <%= rule.when.status %><% } %>\n </td>\n <td class=\"px-4 py-2\">\n <% if (rule.then.requireRole) { %>require role: <%= rule.then.requireRole %><% } %>\n <% if (rule.then.deny) { %>deny<% } %>\n <% if (rule.then.warn) { %>warn: <%= rule.then.warn %><% } %>\n </td>\n </tr>\n <% }); %>\n </tbody>\n </table>\n</div>\n<% } %>`;\n\nexport const ownerTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Owners › <%= owner.id %>\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-2\"><%= owner.displayName %></h1>\n\n<div class=\"grid grid-cols-1 md:grid-cols-3 gap-4 mb-6\">\n <div class=\"bg-white rounded-lg shadow p-4\">\n <div class=\"text-sm text-gray-500\">ID</div>\n <div><%= owner.id %></div>\n </div>\n <% if (owner.email) { %>\n <div class=\"bg-white rounded-lg shadow p-4\">\n <div class=\"text-sm text-gray-500\">Email</div>\n <div><a href=\"mailto:<%= owner.email %>\" class=\"text-blue-600 hover:underline\"><%= owner.email %></a></div>\n </div>\n <% } %>\n <% if (owner.team) { %>\n <div class=\"bg-white rounded-lg shadow p-4\">\n <div class=\"text-sm text-gray-500\">Team</div>\n <div><%= owner.team %></div>\n </div>\n <% } %>\n</div>\n\n<% if (ownedNodes.length > 0) { %>\n<h2 class=\"text-xl font-semibold mb-3\">Owned Nodes</h2>\n<ul class=\"space-y-1\">\n <% ownedNodes.forEach(function(node) { %>\n <li>\n <span class=\"inline-block bg-gray-100 text-xs px-1.5 py-0.5 rounded mr-1\"><%= node.kind %></span>\n <a href=\"<%= node.href %>\" class=\"text-blue-600 hover:underline\"><%= node.id %></a>\n </li>\n <% }); %>\n</ul>\n<% } %>`;\n\nexport const glossaryTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Glossary\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-6\">Glossary</h1>\n\n<% if (terms.length === 0) { %>\n<p class=\"text-gray-500\">No terms defined.</p>\n<% } else { %>\n<div class=\"overflow-x-auto\">\n <table class=\"min-w-full bg-white rounded-lg shadow text-sm\">\n <thead>\n <tr class=\"bg-gray-50 text-left\">\n <th class=\"px-4 py-2\">Term</th>\n <th class=\"px-4 py-2\">Definition</th>\n <th class=\"px-4 py-2\">Synonyms</th>\n <th class=\"px-4 py-2\">Maps To</th>\n </tr>\n </thead>\n <tbody>\n <% terms.forEach(function(term) { %>\n <tr class=\"border-t\">\n <td class=\"px-4 py-2 font-medium\"><%= term.id %></td>\n <td class=\"px-4 py-2\"><%= term.definition %></td>\n <td class=\"px-4 py-2\"><%= (term.synonyms || []).join(', ') %></td>\n <td class=\"px-4 py-2\">\n <% (term.mapsTo || []).forEach(function(target) { %>\n <a href=\"<%= basePath %>/concepts/<%= target %>.html\" class=\"text-blue-600 hover:underline\"><%= target %></a><%= ' ' %>\n <% }); %>\n </td>\n </tr>\n <% }); %>\n </tbody>\n </table>\n</div>\n<% } %>`;\n\nexport const searchTemplate = `<nav class=\"text-sm text-gray-500 mb-4\">\n <a href=\"<%= basePath %>/\" class=\"hover:text-blue-600\">Home</a> › Search\n</nav>\n\n<h1 class=\"text-3xl font-bold mb-6\">Search</h1>\n\n<input\n id=\"search-input\"\n type=\"text\"\n placeholder=\"Search concepts, products, policies, terms...\"\n class=\"w-full border border-gray-300 rounded-lg px-4 py-2 mb-6 focus:outline-none focus:ring-2 focus:ring-blue-500\"\n>\n\n<div id=\"search-results\" class=\"space-y-3\"></div>\n\n<script>window.__CONTEXTKIT_BASE_PATH__ = '<%= basePath %>';</script>\n<script src=\"<%= basePath %>/search.js\"></script>`;\n","import MiniSearch from 'minisearch';\nimport type { Manifest } from '@runcontext/core';\n\nexport interface SearchDocument {\n id: string;\n kind: string;\n text: string;\n tags: string;\n}\n\n/**\n * Build a MiniSearch index from the manifest and return the serialized JSON string.\n * The client-side search.js will load this JSON and use MiniSearch.loadJSON().\n */\nexport function buildSearchIndex(manifest: Manifest): string {\n const miniSearch = new MiniSearch<SearchDocument>({\n fields: ['id', 'text', 'tags'],\n storeFields: ['id', 'kind', 'text'],\n searchOptions: {\n boost: { id: 2 },\n fuzzy: 0.2,\n prefix: true,\n },\n });\n\n const documents: SearchDocument[] = [];\n\n for (const concept of manifest.concepts) {\n documents.push({\n id: concept.id,\n kind: 'concept',\n text: concept.definition,\n tags: (concept.tags ?? []).join(' '),\n });\n }\n\n for (const product of manifest.products) {\n documents.push({\n id: product.id,\n kind: 'product',\n text: product.description,\n tags: (product.tags ?? []).join(' '),\n });\n }\n\n for (const policy of manifest.policies) {\n documents.push({\n id: policy.id,\n kind: 'policy',\n text: policy.description,\n tags: (policy.tags ?? []).join(' '),\n });\n }\n\n for (const entity of manifest.entities) {\n documents.push({\n id: entity.id,\n kind: 'entity',\n text: entity.definition ?? '',\n tags: (entity.tags ?? []).join(' '),\n });\n }\n\n for (const term of manifest.terms) {\n documents.push({\n id: term.id,\n kind: 'term',\n text: term.definition,\n tags: (term.tags ?? []).join(' '),\n });\n }\n\n miniSearch.addAll(documents);\n\n return JSON.stringify(miniSearch);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/generator.ts","../src/templates.ts","../src/search/build-index.ts"],"sourcesContent":["/**\n * Site generator for ContextKit.\n *\n * `generateSite` accepts a Manifest and returns a Map of relative file path\n * to HTML content. The caller is responsible for writing files to disk.\n *\n * `buildSite` is a convenience function used by the CLI that compiles context,\n * generates the site, and writes files to the output directory.\n */\n\nimport ejs from 'ejs';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Manifest, SiteConfig, ContextKitConfig } from '@runcontext/core';\nimport {\n indexTemplate,\n modelTemplate,\n schemaTemplate,\n rulesTemplate,\n glossaryTemplate,\n ownerTemplate,\n searchTemplate,\n} from './templates.js';\nimport { buildSearchIndex } from './search/build-index.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface GenerateSiteOptions {\n manifest: Manifest;\n config?: SiteConfig;\n}\n\n// ---------------------------------------------------------------------------\n// generateSite — pure function returning Map<path, html>\n// ---------------------------------------------------------------------------\n\n/**\n * Generate all HTML pages for the documentation site.\n *\n * @param manifest - The compiled ContextKit manifest\n * @param config - Optional site configuration (title, base_path)\n * @returns Map of relative file paths to HTML content\n */\nexport function generateSite(\n manifest: Manifest,\n config?: SiteConfig,\n): Map<string, string> {\n const files = new Map<string, string>();\n const siteTitle = config?.title ?? 'ContextKit';\n const basePath = (config?.base_path ?? '').replace(/\\/+$/, '');\n\n const commonData = {\n siteTitle,\n basePath,\n };\n\n // --- Index page ---\n files.set(\n 'index.html',\n ejs.render(indexTemplate, {\n ...commonData,\n pageTitle: 'Home',\n models: manifest.models,\n governance: manifest.governance,\n tiers: manifest.tiers,\n owners: manifest.owners,\n }),\n );\n\n // --- Model pages ---\n for (const [name, model] of Object.entries(manifest.models)) {\n const gov = manifest.governance[name] ?? null;\n const tier = manifest.tiers[name] ?? null;\n const rules = manifest.rules[name] ?? null;\n\n // Main model page\n files.set(\n `models/${name}.html`,\n ejs.render(modelTemplate, {\n ...commonData,\n pageTitle: name,\n model,\n gov,\n tier,\n rules,\n }),\n );\n\n // Schema browser page\n files.set(\n `models/${name}/schema.html`,\n ejs.render(schemaTemplate, {\n ...commonData,\n pageTitle: `${name} — Schema`,\n model,\n gov,\n tier,\n }),\n );\n\n // Rules page\n files.set(\n `models/${name}/rules.html`,\n ejs.render(rulesTemplate, {\n ...commonData,\n pageTitle: `${name} — Rules`,\n modelName: name,\n rules,\n }),\n );\n }\n\n // --- Glossary page ---\n files.set(\n 'glossary.html',\n ejs.render(glossaryTemplate, {\n ...commonData,\n pageTitle: 'Glossary',\n terms: manifest.terms,\n }),\n );\n\n // --- Owner pages ---\n for (const [oid, owner] of Object.entries(manifest.owners)) {\n // Find models governed by this owner\n const governedModels: Array<{ name: string; tier: string | null }> = [];\n for (const [modelName, gov] of Object.entries(manifest.governance)) {\n if (gov.owner === oid) {\n const tierScore = manifest.tiers[modelName];\n governedModels.push({\n name: modelName,\n tier: tierScore?.tier ?? null,\n });\n }\n }\n\n files.set(\n `owners/${oid}.html`,\n ejs.render(ownerTemplate, {\n ...commonData,\n pageTitle: owner.display_name,\n owner,\n governedModels,\n }),\n );\n }\n\n // --- Search page ---\n const searchIndex = buildSearchIndex(manifest, basePath);\n files.set(\n 'search.html',\n ejs.render(searchTemplate, {\n ...commonData,\n pageTitle: 'Search',\n searchIndexJson: JSON.stringify(searchIndex),\n }),\n );\n\n // --- Search index JSON (for programmatic access) ---\n files.set('search-index.json', JSON.stringify(searchIndex, null, 2));\n\n return files;\n}\n\n// ---------------------------------------------------------------------------\n// buildSite — convenience function used by CLI\n// ---------------------------------------------------------------------------\n\n/**\n * Build the documentation site and write files to disk.\n *\n * Called by the CLI `site` command. Accepts a manifest, config, and output\n * directory, then generates and writes all site files.\n *\n * @param manifest - The compiled ContextKit manifest\n * @param config - The full ContextKit configuration\n * @param outputDir - Directory to write site files to\n */\nexport async function buildSite(\n manifest: Manifest,\n config: ContextKitConfig,\n outputDir: string,\n): Promise<void> {\n const siteConfig = config.site;\n const files = generateSite(manifest, siteConfig);\n\n for (const [filePath, content] of files) {\n const fullPath = path.join(outputDir, filePath);\n const dir = path.dirname(fullPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n fs.writeFileSync(fullPath, content, 'utf-8');\n }\n}\n","/**\n * EJS template strings for the ContextKit documentation site.\n *\n * All templates use Tailwind CDN for styling and are rendered\n * via `ejs.render()` with embedded template strings.\n */\n\n// ---------------------------------------------------------------------------\n// Shared layout helpers\n// ---------------------------------------------------------------------------\n\nconst HEAD = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title><%= pageTitle %> — <%= siteTitle %></title>\n <script src=\"https://cdn.tailwindcss.com\"></script>\n</head>`;\n\nconst NAV = `<nav class=\"bg-gray-900 text-white px-6 py-3 flex items-center gap-6\">\n <a href=\"<%= basePath %>/\" class=\"font-bold text-lg\"><%- siteTitle %></a>\n <a href=\"<%= basePath %>/\" class=\"hover:underline\">Models</a>\n <a href=\"<%= basePath %>/glossary.html\" class=\"hover:underline\">Glossary</a>\n <a href=\"<%= basePath %>/search.html\" class=\"hover:underline\">Search</a>\n</nav>`;\n\nconst FOOTER = `<footer class=\"mt-12 border-t py-4 px-6 text-gray-500 text-sm\">\n Generated by <a href=\"https://github.com/erickittelson/ContextKit\" class=\"underline\">ContextKit</a>\n</footer>`;\n\n// ---------------------------------------------------------------------------\n// Tier badge helper (used in multiple templates)\n// ---------------------------------------------------------------------------\n\nconst TIER_BADGE = `<% function tierBadge(tier) {\n var colors = { none: 'bg-gray-200 text-gray-700', bronze: 'bg-amber-700 text-white', silver: 'bg-gray-400 text-white', gold: 'bg-yellow-400 text-black' };\n var cls = colors[tier] || colors.none;\n return '<span class=\"px-2 py-0.5 rounded text-xs font-semibold uppercase ' + cls + '\">' + tier + '</span>';\n} %>`;\n\n// ---------------------------------------------------------------------------\n// Index (home) template\n// ---------------------------------------------------------------------------\n\nexport const indexTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n${TIER_BADGE}\n<main class=\"max-w-5xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-6\"><%- siteTitle %></h1>\n\n <section>\n <h2 class=\"text-xl font-semibold mb-4\">Models</h2>\n <% if (Object.keys(models).length === 0) { %>\n <p class=\"text-gray-500\">No models found.</p>\n <% } else { %>\n <div class=\"grid gap-4\">\n <% for (var name of Object.keys(models)) { %>\n <div class=\"border rounded-lg p-4 hover:shadow transition\">\n <div class=\"flex items-center gap-3\">\n <a href=\"<%= basePath %>/models/<%= name %>.html\" class=\"text-lg font-medium text-blue-600 hover:underline\"><%= name %></a>\n <% if (tiers[name]) { %><%- tierBadge(tiers[name].tier) %><% } %>\n </div>\n <% if (models[name].description) { %>\n <p class=\"text-gray-600 mt-1\"><%= models[name].description %></p>\n <% } %>\n <% if (governance[name]) { %>\n <div class=\"flex gap-2 mt-2 text-xs text-gray-500\">\n <% if (governance[name].owner) { %>\n <span>Owner: <a href=\"<%= basePath %>/owners/<%= governance[name].owner %>.html\" class=\"underline\"><%= governance[name].owner %></a></span>\n <% } %>\n <% if (governance[name].trust) { %>\n <span>Trust: <%= governance[name].trust %></span>\n <% } %>\n </div>\n <% } %>\n </div>\n <% } %>\n </div>\n <% } %>\n </section>\n\n <% if (Object.keys(owners).length > 0) { %>\n <section class=\"mt-8\">\n <h2 class=\"text-xl font-semibold mb-4\">Owners</h2>\n <ul class=\"space-y-1\">\n <% for (var oid of Object.keys(owners)) { %>\n <li><a href=\"<%= basePath %>/owners/<%= oid %>.html\" class=\"text-blue-600 hover:underline\"><%= owners[oid].display_name %></a></li>\n <% } %>\n </ul>\n </section>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Model page template\n// ---------------------------------------------------------------------------\n\nexport const modelTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n${TIER_BADGE}\n<main class=\"max-w-5xl mx-auto p-6\">\n <div class=\"flex items-center gap-3 mb-2\">\n <h1 class=\"text-3xl font-bold\"><%= model.name %></h1>\n <% if (tier) { %><%- tierBadge(tier.tier) %><% } %>\n </div>\n <% if (model.description) { %>\n <p class=\"text-gray-600 mb-4\"><%= model.description %></p>\n <% } %>\n\n <div class=\"flex gap-3 mb-6 text-sm\">\n <a href=\"<%= basePath %>/models/<%= model.name %>/schema.html\" class=\"text-blue-600 hover:underline\">Schema Browser</a>\n <a href=\"<%= basePath %>/models/<%= model.name %>/rules.html\" class=\"text-blue-600 hover:underline\">Rules & Queries</a>\n </div>\n\n <% if (gov) { %>\n <section class=\"mb-6\">\n <h2 class=\"text-xl font-semibold mb-2\">Governance</h2>\n <table class=\"table-auto border-collapse text-sm\">\n <tbody>\n <tr><td class=\"pr-4 font-medium\">Owner</td><td><a href=\"<%= basePath %>/owners/<%= gov.owner %>.html\" class=\"text-blue-600 underline\"><%= gov.owner %></a></td></tr>\n <% if (gov.trust) { %><tr><td class=\"pr-4 font-medium\">Trust</td><td><%= gov.trust %></td></tr><% } %>\n <% if (gov.security) { %><tr><td class=\"pr-4 font-medium\">Security</td><td><%= gov.security %></td></tr><% } %>\n <% if (gov.tags && gov.tags.length > 0) { %><tr><td class=\"pr-4 font-medium\">Tags</td><td><%= gov.tags.join(', ') %></td></tr><% } %>\n </tbody>\n </table>\n </section>\n <% } %>\n\n <% if (model.datasets && model.datasets.length > 0) { %>\n <section class=\"mb-6\">\n <h2 class=\"text-xl font-semibold mb-2\">Datasets</h2>\n <div class=\"space-y-3\">\n <% for (var ds of model.datasets) { %>\n <div class=\"border rounded p-3\">\n <div class=\"font-medium\"><%= ds.name %></div>\n <div class=\"text-xs text-gray-500\">Source: <%= ds.source %></div>\n <% if (ds.description) { %><p class=\"text-sm text-gray-600 mt-1\"><%= ds.description %></p><% } %>\n <% if (ds.fields && ds.fields.length > 0) { %>\n <div class=\"text-xs text-gray-500 mt-1\"><%= ds.fields.length %> field(s)</div>\n <% } %>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (model.relationships && model.relationships.length > 0) { %>\n <section class=\"mb-6\">\n <h2 class=\"text-xl font-semibold mb-2\">Relationships</h2>\n <table class=\"table-auto w-full text-sm border-collapse\">\n <thead>\n <tr class=\"border-b\"><th class=\"text-left py-1 pr-3\">Name</th><th class=\"text-left py-1 pr-3\">From</th><th class=\"text-left py-1\">To</th></tr>\n </thead>\n <tbody>\n <% for (var rel of model.relationships) { %>\n <tr class=\"border-b\"><td class=\"py-1 pr-3\"><%= rel.name %></td><td class=\"py-1 pr-3\"><%= rel.from %></td><td class=\"py-1\"><%= rel.to %></td></tr>\n <% } %>\n </tbody>\n </table>\n </section>\n <% } %>\n\n <% if (model.metrics && model.metrics.length > 0) { %>\n <section class=\"mb-6\">\n <h2 class=\"text-xl font-semibold mb-2\">Metrics</h2>\n <div class=\"space-y-2\">\n <% for (var metric of model.metrics) { %>\n <div class=\"border rounded p-3\">\n <div class=\"font-medium\"><%= metric.name %></div>\n <% if (metric.description) { %><p class=\"text-sm text-gray-600\"><%= metric.description %></p><% } %>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (tier) { %>\n <section class=\"mb-6\">\n <h2 class=\"text-xl font-semibold mb-2\">Tier Details</h2>\n <% var tierLevels = ['bronze', 'silver', 'gold']; %>\n <% for (var lvl of tierLevels) { %>\n <div class=\"mb-3\">\n <h3 class=\"font-medium capitalize flex items-center gap-2\">\n <%= lvl %>\n <% if (tier[lvl].passed) { %>\n <span class=\"text-green-600 text-xs\">Passed</span>\n <% } else { %>\n <span class=\"text-red-500 text-xs\">Not passed</span>\n <% } %>\n </h3>\n <% if (tier[lvl].checks && tier[lvl].checks.length > 0) { %>\n <ul class=\"text-sm ml-4 list-disc\">\n <% for (var chk of tier[lvl].checks) { %>\n <li class=\"<%= chk.passed ? 'text-green-700' : 'text-red-600' %>\"><%= chk.label %><% if (chk.detail) { %> — <%= chk.detail %><% } %></li>\n <% } %>\n </ul>\n <% } %>\n </div>\n <% } %>\n </section>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Schema browser template\n// ---------------------------------------------------------------------------\n\nexport const schemaTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n${TIER_BADGE}\n<main class=\"max-w-5xl mx-auto p-6\">\n <div class=\"flex items-center gap-3 mb-2\">\n <h1 class=\"text-3xl font-bold\"><%= model.name %> — Schema Browser</h1>\n <% if (tier) { %><%- tierBadge(tier.tier) %><% } %>\n </div>\n <div class=\"mb-4 text-sm\">\n <a href=\"<%= basePath %>/models/<%= model.name %>.html\" class=\"text-blue-600 hover:underline\">← Back to model</a>\n </div>\n\n <% if (model.datasets && model.datasets.length > 0) { %>\n <% for (var ds of model.datasets) { %>\n <section class=\"mb-8 border rounded-lg p-4\">\n <h2 class=\"text-xl font-semibold mb-1\"><%= ds.name %></h2>\n <div class=\"text-xs text-gray-500 mb-2\">Source: <%= ds.source %></div>\n <% if (ds.description) { %><p class=\"text-sm text-gray-600 mb-3\"><%= ds.description %></p><% } %>\n\n <% var dsGov = gov && gov.datasets && gov.datasets[ds.name]; %>\n <% if (dsGov) { %>\n <div class=\"text-xs text-gray-500 mb-3 flex gap-4\">\n <% if (dsGov.grain) { %><span>Grain: <strong><%= dsGov.grain %></strong></span><% } %>\n <% if (dsGov.table_type) { %><span>Type: <strong><%= dsGov.table_type %></strong></span><% } %>\n <% if (dsGov.refresh) { %><span>Refresh: <strong><%= dsGov.refresh %></strong></span><% } %>\n <% if (dsGov.security) { %><span>Security: <strong><%= dsGov.security %></strong></span><% } %>\n </div>\n <% } %>\n\n <% if (ds.fields && ds.fields.length > 0) { %>\n <table class=\"table-auto w-full text-sm border-collapse\">\n <thead>\n <tr class=\"border-b bg-gray-50\">\n <th class=\"text-left py-2 px-2\">Field</th>\n <th class=\"text-left py-2 px-2\">Description</th>\n <th class=\"text-left py-2 px-2\">Semantic Role</th>\n <th class=\"text-left py-2 px-2\">Aggregation</th>\n </tr>\n </thead>\n <tbody>\n <% for (var field of ds.fields) { %>\n <% var fieldKey = ds.name + '.' + field.name; %>\n <% var fGov = gov && gov.fields && gov.fields[fieldKey]; %>\n <tr class=\"border-b\">\n <td class=\"py-1 px-2 font-mono text-xs\"><%= field.name %></td>\n <td class=\"py-1 px-2\"><%= field.description || '' %></td>\n <td class=\"py-1 px-2\"><%= fGov && fGov.semantic_role ? fGov.semantic_role : '' %></td>\n <td class=\"py-1 px-2\"><%= fGov && fGov.default_aggregation ? fGov.default_aggregation : '' %></td>\n </tr>\n <% } %>\n </tbody>\n </table>\n <% } else { %>\n <p class=\"text-gray-400 text-sm\">No fields defined.</p>\n <% } %>\n </section>\n <% } %>\n <% } else { %>\n <p class=\"text-gray-500\">No datasets found.</p>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Rules page template (golden queries, business rules, guardrails)\n// ---------------------------------------------------------------------------\n\nexport const rulesTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n<main class=\"max-w-5xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-2\"><%= modelName %> — Rules & Queries</h1>\n <div class=\"mb-4 text-sm\">\n <a href=\"<%= basePath %>/models/<%= modelName %>.html\" class=\"text-blue-600 hover:underline\">← Back to model</a>\n </div>\n\n <% if (rules && rules.golden_queries && rules.golden_queries.length > 0) { %>\n <section class=\"mb-8\">\n <h2 class=\"text-xl font-semibold mb-3\">Golden Queries</h2>\n <div class=\"space-y-4\">\n <% for (var gq of rules.golden_queries) { %>\n <div class=\"border rounded-lg p-4\">\n <div class=\"font-medium mb-2\"><%= gq.question %></div>\n <pre class=\"bg-gray-100 rounded p-3 text-sm overflow-x-auto\"><code><%= gq.sql %></code></pre>\n <% if (gq.dialect) { %><div class=\"text-xs text-gray-500 mt-1\">Dialect: <%= gq.dialect %></div><% } %>\n <% if (gq.tags && gq.tags.length > 0) { %><div class=\"text-xs text-gray-500 mt-1\">Tags: <%= gq.tags.join(', ') %></div><% } %>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (rules && rules.business_rules && rules.business_rules.length > 0) { %>\n <section class=\"mb-8\">\n <h2 class=\"text-xl font-semibold mb-3\">Business Rules</h2>\n <div class=\"space-y-3\">\n <% for (var br of rules.business_rules) { %>\n <div class=\"border rounded p-4\">\n <div class=\"font-medium\"><%= br.name %></div>\n <p class=\"text-sm text-gray-600 mt-1\"><%= br.definition %></p>\n <% if (br.enforcement && br.enforcement.length > 0) { %>\n <div class=\"text-xs text-gray-500 mt-1\">Enforcement: <%= br.enforcement.join(', ') %></div>\n <% } %>\n <% if (br.avoid && br.avoid.length > 0) { %>\n <div class=\"text-xs text-red-500 mt-1\">Avoid: <%= br.avoid.join(', ') %></div>\n <% } %>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (rules && rules.guardrail_filters && rules.guardrail_filters.length > 0) { %>\n <section class=\"mb-8\">\n <h2 class=\"text-xl font-semibold mb-3\">Guardrail Filters</h2>\n <div class=\"space-y-3\">\n <% for (var gf of rules.guardrail_filters) { %>\n <div class=\"border rounded p-4\">\n <div class=\"font-medium\"><%= gf.name %></div>\n <pre class=\"bg-gray-100 rounded p-2 text-sm mt-1\"><code><%= gf.filter %></code></pre>\n <p class=\"text-sm text-gray-600 mt-1\"><%= gf.reason %></p>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (rules && rules.hierarchies && rules.hierarchies.length > 0) { %>\n <section class=\"mb-8\">\n <h2 class=\"text-xl font-semibold mb-3\">Hierarchies</h2>\n <div class=\"space-y-3\">\n <% for (var h of rules.hierarchies) { %>\n <div class=\"border rounded p-4\">\n <div class=\"font-medium\"><%= h.name %></div>\n <div class=\"text-sm text-gray-600 mt-1\">Dataset: <%= h.dataset %></div>\n <div class=\"text-sm text-gray-600\">Levels: <%= h.levels.join(' → ') %></div>\n </div>\n <% } %>\n </div>\n </section>\n <% } %>\n\n <% if (!rules || ((!rules.golden_queries || rules.golden_queries.length === 0) && (!rules.business_rules || rules.business_rules.length === 0) && (!rules.guardrail_filters || rules.guardrail_filters.length === 0) && (!rules.hierarchies || rules.hierarchies.length === 0))) { %>\n <p class=\"text-gray-500\">No rules or queries defined for this model.</p>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Glossary template\n// ---------------------------------------------------------------------------\n\nexport const glossaryTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n<main class=\"max-w-5xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-6\">Glossary</h1>\n\n <% var termIds = Object.keys(terms).sort(); %>\n <% if (termIds.length === 0) { %>\n <p class=\"text-gray-500\">No terms defined.</p>\n <% } else { %>\n <div class=\"space-y-4\">\n <% for (var tid of termIds) { %>\n <% var term = terms[tid]; %>\n <div class=\"border rounded-lg p-4\" id=\"term-<%= tid %>\">\n <h2 class=\"text-lg font-semibold\"><%= tid %></h2>\n <p class=\"text-gray-700 mt-1\"><%= term.definition %></p>\n <% if (term.synonyms && term.synonyms.length > 0) { %>\n <div class=\"text-sm text-gray-500 mt-1\">Synonyms: <%= term.synonyms.join(', ') %></div>\n <% } %>\n <% if (term.maps_to && term.maps_to.length > 0) { %>\n <div class=\"text-sm text-gray-500 mt-1\">Maps to: <%= term.maps_to.join(', ') %></div>\n <% } %>\n <% if (term.owner) { %>\n <div class=\"text-sm text-gray-500 mt-1\">Owner: <a href=\"<%= basePath %>/owners/<%= term.owner %>.html\" class=\"text-blue-600 underline\"><%= term.owner %></a></div>\n <% } %>\n </div>\n <% } %>\n </div>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Owner page template\n// ---------------------------------------------------------------------------\n\nexport const ownerTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n${TIER_BADGE}\n<main class=\"max-w-5xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-2\"><%= owner.display_name %></h1>\n <div class=\"text-sm text-gray-500 mb-4\">\n <% if (owner.email) { %><span>Email: <%= owner.email %></span><% } %>\n <% if (owner.team) { %><span class=\"ml-4\">Team: <%= owner.team %></span><% } %>\n </div>\n <% if (owner.description) { %>\n <p class=\"text-gray-600 mb-4\"><%= owner.description %></p>\n <% } %>\n\n <% if (governedModels.length > 0) { %>\n <section>\n <h2 class=\"text-xl font-semibold mb-3\">Governed Models</h2>\n <div class=\"space-y-2\">\n <% for (var gm of governedModels) { %>\n <div class=\"flex items-center gap-3\">\n <a href=\"<%= basePath %>/models/<%= gm.name %>.html\" class=\"text-blue-600 hover:underline\"><%= gm.name %></a>\n <% if (gm.tier) { %><%- tierBadge(gm.tier) %><% } %>\n </div>\n <% } %>\n </div>\n </section>\n <% } else { %>\n <p class=\"text-gray-500\">No models governed by this owner.</p>\n <% } %>\n</main>\n${FOOTER}\n</body>\n</html>`;\n\n// ---------------------------------------------------------------------------\n// Search page template (client-side MiniSearch)\n// ---------------------------------------------------------------------------\n\nexport const searchTemplate = `${HEAD}\n<body class=\"bg-white text-gray-900 min-h-screen\">\n${NAV}\n<main class=\"max-w-5xl mx-auto p-6\">\n <h1 class=\"text-3xl font-bold mb-6\">Search</h1>\n\n <input type=\"text\" id=\"search-input\"\n placeholder=\"Search models, datasets, terms...\"\n class=\"w-full border rounded-lg px-4 py-2 mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500\" />\n\n <div id=\"search-results\" class=\"space-y-2\"></div>\n</main>\n${FOOTER}\n\n<script src=\"https://cdn.jsdelivr.net/npm/minisearch@7.1.0/dist/umd/index.min.js\"></script>\n<script>\n(function() {\n var indexData = <%- searchIndexJson %>;\n var miniSearch = MiniSearch.loadJSON(JSON.stringify(indexData.index), indexData.options);\n\n var input = document.getElementById('search-input');\n var resultsContainer = document.getElementById('search-results');\n var docs = indexData.documents;\n\n function clearResults() {\n while (resultsContainer.firstChild) {\n resultsContainer.removeChild(resultsContainer.firstChild);\n }\n }\n\n function createResultElement(doc) {\n var wrapper = document.createElement('div');\n wrapper.className = 'border rounded p-3';\n\n var link = document.createElement('a');\n link.href = doc.url;\n link.className = 'text-blue-600 font-medium hover:underline';\n link.textContent = doc.title;\n wrapper.appendChild(link);\n\n var badge = document.createElement('span');\n badge.className = 'ml-2 text-xs text-gray-400 uppercase';\n badge.textContent = doc.type;\n wrapper.appendChild(badge);\n\n if (doc.description) {\n var desc = document.createElement('p');\n desc.className = 'text-sm text-gray-600 mt-1';\n desc.textContent = doc.description;\n wrapper.appendChild(desc);\n }\n\n return wrapper;\n }\n\n input.addEventListener('input', function() {\n var query = input.value.trim();\n clearResults();\n if (!query) return;\n var hits = miniSearch.search(query, { prefix: true, fuzzy: 0.2 });\n if (hits.length === 0) {\n var noResults = document.createElement('p');\n noResults.className = 'text-gray-500';\n noResults.textContent = 'No results found.';\n resultsContainer.appendChild(noResults);\n return;\n }\n hits.slice(0, 20).forEach(function(hit) {\n var doc = docs[hit.id];\n if (doc) {\n resultsContainer.appendChild(createResultElement(doc));\n }\n });\n });\n})();\n</script>\n</body>\n</html>`;\n","/**\n * Builds a MiniSearch index from a Manifest for client-side search.\n *\n * The index is serialized to JSON so it can be embedded in the search page\n * and loaded by MiniSearch on the client side.\n */\n\nimport MiniSearch from 'minisearch';\nimport type { Manifest } from '@runcontext/core';\n\nexport interface SearchDocument {\n id: string;\n type: string;\n title: string;\n description: string;\n url: string;\n}\n\nexport interface SearchIndex {\n /** Serialized MiniSearch index (JSON-parsed object). */\n index: unknown;\n /** MiniSearch constructor options needed to reload the index. */\n options: {\n fields: string[];\n storeFields: string[];\n idField: string;\n };\n /** Map of document ID to document metadata for rendering results. */\n documents: Record<string, SearchDocument>;\n}\n\nconst MINISEARCH_OPTIONS = {\n fields: ['title', 'description', 'type'],\n storeFields: ['title', 'description', 'type', 'url'],\n idField: 'id',\n};\n\n/**\n * Build a search index from a manifest.\n *\n * @param manifest - The compiled ContextKit manifest\n * @param basePath - The base URL path for links (e.g. '' or '/docs')\n * @returns A SearchIndex object ready for JSON serialization\n */\nexport function buildSearchIndex(manifest: Manifest, basePath: string): SearchIndex {\n const docs: SearchDocument[] = [];\n let idCounter = 0;\n\n // Index models\n for (const [name, model] of Object.entries(manifest.models)) {\n docs.push({\n id: String(idCounter++),\n type: 'model',\n title: name,\n description: model.description ?? '',\n url: `${basePath}/models/${name}.html`,\n });\n\n // Index datasets within each model\n if (model.datasets) {\n for (const ds of model.datasets) {\n docs.push({\n id: String(idCounter++),\n type: 'dataset',\n title: `${name} / ${ds.name}`,\n description: ds.description ?? '',\n url: `${basePath}/models/${name}/schema.html`,\n });\n }\n }\n }\n\n // Index glossary terms\n for (const [termId, term] of Object.entries(manifest.terms)) {\n docs.push({\n id: String(idCounter++),\n type: 'term',\n title: termId,\n description: term.definition,\n url: `${basePath}/glossary.html#term-${termId}`,\n });\n }\n\n // Index owners\n for (const [oid, owner] of Object.entries(manifest.owners)) {\n docs.push({\n id: String(idCounter++),\n type: 'owner',\n title: owner.display_name,\n description: owner.description ?? '',\n url: `${basePath}/owners/${oid}.html`,\n });\n }\n\n // Build MiniSearch index\n const miniSearch = new MiniSearch(MINISEARCH_OPTIONS);\n miniSearch.addAll(docs);\n\n // Create document lookup map by id\n const documentsMap: Record<string, SearchDocument> = {};\n for (const doc of docs) {\n documentsMap[doc.id] = doc;\n }\n\n return {\n index: JSON.parse(JSON.stringify(miniSearch)),\n options: MINISEARCH_OPTIONS,\n documents: documentsMap,\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runcontext/site",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Static documentation site generator for ContextKit",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eric Kittelson",
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
"url": "https://github.com/erickittelson/ContextKit.git",
|
|
11
11
|
"directory": "packages/site"
|
|
12
12
|
},
|
|
13
|
-
"keywords": [
|
|
13
|
+
"keywords": [
|
|
14
|
+
"contextkit",
|
|
15
|
+
"site-generator",
|
|
16
|
+
"documentation",
|
|
17
|
+
"ejs"
|
|
18
|
+
],
|
|
14
19
|
"type": "module",
|
|
15
20
|
"main": "./dist/index.cjs",
|
|
16
21
|
"module": "./dist/index.mjs",
|
|
@@ -27,15 +32,13 @@
|
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
34
|
},
|
|
30
|
-
"files": [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"clean": "rm -rf dist"
|
|
34
|
-
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
35
38
|
"dependencies": {
|
|
36
|
-
"@runcontext/core": "^0.1.1",
|
|
37
39
|
"ejs": "^3.1.10",
|
|
38
|
-
"minisearch": "^7.1.0"
|
|
40
|
+
"minisearch": "^7.1.0",
|
|
41
|
+
"@runcontext/core": "^0.2.1"
|
|
39
42
|
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@types/ejs": "^3.1.5",
|
|
@@ -43,5 +46,9 @@
|
|
|
43
46
|
"tsup": "^8.4.0",
|
|
44
47
|
"typescript": "^5.7.0",
|
|
45
48
|
"vitest": "^3.2.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsup",
|
|
52
|
+
"clean": "rm -rf dist"
|
|
46
53
|
}
|
|
47
|
-
}
|
|
54
|
+
}
|