@runcontext/site 0.1.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,549 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/generator.ts
2
+ var _promises = require('fs/promises');
3
+ var _path = require('path');
4
+ var _url = require('url');
5
+ var _ejs = require('ejs'); var _ejs2 = _interopRequireDefault(_ejs);
6
+
7
+ // src/templates.ts
8
+ var layoutTemplate = `<!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title><%= pageTitle %> - <%= project.displayName %></title>
14
+ <script src="https://cdn.tailwindcss.com"></script>
15
+ <link rel="stylesheet" href="<%= basePath %>/style.css">
16
+ </head>
17
+ <body class="bg-gray-50 text-gray-900 min-h-screen flex flex-col">
18
+ <nav class="bg-white border-b border-gray-200 px-6 py-3">
19
+ <div class="max-w-6xl mx-auto flex items-center gap-6">
20
+ <a href="<%= basePath %>/" class="font-bold text-lg text-blue-600"><%= project.displayName %></a>
21
+ <div class="flex gap-4 text-sm">
22
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a>
23
+ <a href="<%= basePath %>/glossary.html" class="hover:text-blue-600">Glossary</a>
24
+ <a href="<%= basePath %>/search.html" class="hover:text-blue-600">Search</a>
25
+ </div>
26
+ </div>
27
+ </nav>
28
+ <main class="flex-1 max-w-6xl mx-auto w-full px-6 py-8">
29
+ <%- content %>
30
+ </main>
31
+ <footer class="bg-white border-t border-gray-200 px-6 py-4 text-center text-sm text-gray-500">
32
+ Built with <a href="https://github.com/contextkit" class="text-blue-600 hover:underline">ContextKit</a> &middot; <%= build.timestamp %>
33
+ </footer>
34
+ </body>
35
+ </html>`;
36
+ var indexTemplate = `<h1 class="text-3xl font-bold mb-2"><%= project.displayName %></h1>
37
+ <p class="text-gray-600 mb-6">Version <%= project.version %></p>
38
+
39
+ <div class="grid grid-cols-2 md:grid-cols-3 gap-4 mb-8">
40
+ <div class="bg-white rounded-lg shadow p-4 text-center">
41
+ <div class="text-2xl font-bold text-blue-600"><%= concepts.length %></div>
42
+ <div class="text-sm text-gray-500">Concepts</div>
43
+ </div>
44
+ <div class="bg-white rounded-lg shadow p-4 text-center">
45
+ <div class="text-2xl font-bold text-green-600"><%= products.length %></div>
46
+ <div class="text-sm text-gray-500">Products</div>
47
+ </div>
48
+ <div class="bg-white rounded-lg shadow p-4 text-center">
49
+ <div class="text-2xl font-bold text-purple-600"><%= policies.length %></div>
50
+ <div class="text-sm text-gray-500">Policies</div>
51
+ </div>
52
+ <div class="bg-white rounded-lg shadow p-4 text-center">
53
+ <div class="text-2xl font-bold text-orange-600"><%= entities.length %></div>
54
+ <div class="text-sm text-gray-500">Entities</div>
55
+ </div>
56
+ <div class="bg-white rounded-lg shadow p-4 text-center">
57
+ <div class="text-2xl font-bold text-teal-600"><%= terms.length %></div>
58
+ <div class="text-sm text-gray-500">Terms</div>
59
+ </div>
60
+ <div class="bg-white rounded-lg shadow p-4 text-center">
61
+ <div class="text-2xl font-bold text-red-600"><%= owners.length %></div>
62
+ <div class="text-sm text-gray-500">Owners</div>
63
+ </div>
64
+ </div>
65
+
66
+ <% if (concepts.length > 0) { %>
67
+ <h2 class="text-xl font-semibold mb-3">Concepts</h2>
68
+ <ul class="mb-6 space-y-1">
69
+ <% concepts.forEach(function(c) { %>
70
+ <li><a href="<%= basePath %>/concepts/<%= c.id %>.html" class="text-blue-600 hover:underline"><%= c.id %></a>
71
+ <% if (c.certified) { %><span class="ml-1 text-xs bg-green-100 text-green-800 px-1.5 py-0.5 rounded">certified</span><% } %>
72
+ </li>
73
+ <% }); %>
74
+ </ul>
75
+ <% } %>
76
+
77
+ <% if (products.length > 0) { %>
78
+ <h2 class="text-xl font-semibold mb-3">Products</h2>
79
+ <ul class="mb-6 space-y-1">
80
+ <% products.forEach(function(p) { %>
81
+ <li><a href="<%= basePath %>/products/<%= p.id %>.html" class="text-blue-600 hover:underline"><%= p.id %></a></li>
82
+ <% }); %>
83
+ </ul>
84
+ <% } %>
85
+
86
+ <% if (policies.length > 0) { %>
87
+ <h2 class="text-xl font-semibold mb-3">Policies</h2>
88
+ <ul class="mb-6 space-y-1">
89
+ <% policies.forEach(function(p) { %>
90
+ <li><a href="<%= basePath %>/policies/<%= p.id %>.html" class="text-blue-600 hover:underline"><%= p.id %></a></li>
91
+ <% }); %>
92
+ </ul>
93
+ <% } %>
94
+
95
+ <% if (owners.length > 0) { %>
96
+ <h2 class="text-xl font-semibold mb-3">Owners</h2>
97
+ <ul class="mb-6 space-y-1">
98
+ <% owners.forEach(function(o) { %>
99
+ <li><a href="<%= basePath %>/owners/<%= o.id %>.html" class="text-blue-600 hover:underline"><%= o.displayName %></a></li>
100
+ <% }); %>
101
+ </ul>
102
+ <% } %>`;
103
+ var conceptTemplate = `<nav class="text-sm text-gray-500 mb-4">
104
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Concepts &rsaquo; <%= concept.id %>
105
+ </nav>
106
+
107
+ <h1 class="text-3xl font-bold mb-2"><%= concept.id %>
108
+ <% if (concept.certified) { %><span class="ml-2 text-sm bg-green-100 text-green-800 px-2 py-0.5 rounded">certified</span><% } %>
109
+ </h1>
110
+
111
+ <p class="text-lg text-gray-700 mb-6"><%= concept.definition %></p>
112
+
113
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
114
+ <% if (concept.owner) { %>
115
+ <div class="bg-white rounded-lg shadow p-4">
116
+ <div class="text-sm text-gray-500">Owner</div>
117
+ <a href="<%= basePath %>/owners/<%= concept.owner %>.html" class="text-blue-600 hover:underline"><%= concept.owner %></a>
118
+ </div>
119
+ <% } %>
120
+ <% if (concept.productId) { %>
121
+ <div class="bg-white rounded-lg shadow p-4">
122
+ <div class="text-sm text-gray-500">Product</div>
123
+ <a href="<%= basePath %>/products/<%= concept.productId %>.html" class="text-blue-600 hover:underline"><%= concept.productId %></a>
124
+ </div>
125
+ <% } %>
126
+ </div>
127
+
128
+ <% if (concept.tags && concept.tags.length > 0) { %>
129
+ <div class="mb-4">
130
+ <span class="text-sm text-gray-500 mr-2">Tags:</span>
131
+ <% concept.tags.forEach(function(tag) { %>
132
+ <span class="inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1"><%= tag %></span>
133
+ <% }); %>
134
+ </div>
135
+ <% } %>
136
+
137
+ <% if (concept.dependsOn && concept.dependsOn.length > 0) { %>
138
+ <h2 class="text-xl font-semibold mb-3">Dependencies</h2>
139
+ <ul class="mb-6 space-y-1">
140
+ <% concept.dependsOn.forEach(function(dep) { %>
141
+ <li><a href="<%= basePath %>/concepts/<%= dep %>.html" class="text-blue-600 hover:underline"><%= dep %></a></li>
142
+ <% }); %>
143
+ </ul>
144
+ <% } %>`;
145
+ var productTemplate = `<nav class="text-sm text-gray-500 mb-4">
146
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Products &rsaquo; <%= product.id %>
147
+ </nav>
148
+
149
+ <h1 class="text-3xl font-bold mb-2"><%= product.id %></h1>
150
+ <p class="text-lg text-gray-700 mb-6"><%= product.description %></p>
151
+
152
+ <% if (product.owner) { %>
153
+ <div class="mb-4">
154
+ <span class="text-sm text-gray-500 mr-2">Owner:</span>
155
+ <a href="<%= basePath %>/owners/<%= product.owner %>.html" class="text-blue-600 hover:underline"><%= product.owner %></a>
156
+ </div>
157
+ <% } %>
158
+
159
+ <% if (product.tags && product.tags.length > 0) { %>
160
+ <div class="mb-4">
161
+ <span class="text-sm text-gray-500 mr-2">Tags:</span>
162
+ <% product.tags.forEach(function(tag) { %>
163
+ <span class="inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1"><%= tag %></span>
164
+ <% }); %>
165
+ </div>
166
+ <% } %>
167
+
168
+ <% if (relatedConcepts.length > 0) { %>
169
+ <h2 class="text-xl font-semibold mb-3">Concepts</h2>
170
+ <ul class="mb-6 space-y-1">
171
+ <% relatedConcepts.forEach(function(c) { %>
172
+ <li><a href="<%= basePath %>/concepts/<%= c.id %>.html" class="text-blue-600 hover:underline"><%= c.id %></a> &mdash; <%= c.definition %></li>
173
+ <% }); %>
174
+ </ul>
175
+ <% } %>`;
176
+ var policyTemplate = `<nav class="text-sm text-gray-500 mb-4">
177
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Policies &rsaquo; <%= policy.id %>
178
+ </nav>
179
+
180
+ <h1 class="text-3xl font-bold mb-2"><%= policy.id %></h1>
181
+ <p class="text-lg text-gray-700 mb-6"><%= policy.description %></p>
182
+
183
+ <% if (policy.owner) { %>
184
+ <div class="mb-4">
185
+ <span class="text-sm text-gray-500 mr-2">Owner:</span>
186
+ <a href="<%= basePath %>/owners/<%= policy.owner %>.html" class="text-blue-600 hover:underline"><%= policy.owner %></a>
187
+ </div>
188
+ <% } %>
189
+
190
+ <% if (policy.tags && policy.tags.length > 0) { %>
191
+ <div class="mb-4">
192
+ <span class="text-sm text-gray-500 mr-2">Tags:</span>
193
+ <% policy.tags.forEach(function(tag) { %>
194
+ <span class="inline-block bg-gray-100 text-gray-700 text-xs px-2 py-0.5 rounded mr-1"><%= tag %></span>
195
+ <% }); %>
196
+ </div>
197
+ <% } %>
198
+
199
+ <% if (policy.rules && policy.rules.length > 0) { %>
200
+ <h2 class="text-xl font-semibold mb-3">Rules</h2>
201
+ <div class="overflow-x-auto">
202
+ <table class="min-w-full bg-white rounded-lg shadow text-sm">
203
+ <thead>
204
+ <tr class="bg-gray-50 text-left">
205
+ <th class="px-4 py-2">Priority</th>
206
+ <th class="px-4 py-2">When</th>
207
+ <th class="px-4 py-2">Then</th>
208
+ </tr>
209
+ </thead>
210
+ <tbody>
211
+ <% policy.rules.forEach(function(rule) { %>
212
+ <tr class="border-t">
213
+ <td class="px-4 py-2"><%= rule.priority %></td>
214
+ <td class="px-4 py-2">
215
+ <% if (rule.when.tagsAny) { %>tags: <%= rule.when.tagsAny.join(', ') %><% } %>
216
+ <% if (rule.when.conceptIds) { %>concepts: <%= rule.when.conceptIds.join(', ') %><% } %>
217
+ <% if (rule.when.status) { %>status: <%= rule.when.status %><% } %>
218
+ </td>
219
+ <td class="px-4 py-2">
220
+ <% if (rule.then.requireRole) { %>require role: <%= rule.then.requireRole %><% } %>
221
+ <% if (rule.then.deny) { %>deny<% } %>
222
+ <% if (rule.then.warn) { %>warn: <%= rule.then.warn %><% } %>
223
+ </td>
224
+ </tr>
225
+ <% }); %>
226
+ </tbody>
227
+ </table>
228
+ </div>
229
+ <% } %>`;
230
+ var ownerTemplate = `<nav class="text-sm text-gray-500 mb-4">
231
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Owners &rsaquo; <%= owner.id %>
232
+ </nav>
233
+
234
+ <h1 class="text-3xl font-bold mb-2"><%= owner.displayName %></h1>
235
+
236
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
237
+ <div class="bg-white rounded-lg shadow p-4">
238
+ <div class="text-sm text-gray-500">ID</div>
239
+ <div><%= owner.id %></div>
240
+ </div>
241
+ <% if (owner.email) { %>
242
+ <div class="bg-white rounded-lg shadow p-4">
243
+ <div class="text-sm text-gray-500">Email</div>
244
+ <div><a href="mailto:<%= owner.email %>" class="text-blue-600 hover:underline"><%= owner.email %></a></div>
245
+ </div>
246
+ <% } %>
247
+ <% if (owner.team) { %>
248
+ <div class="bg-white rounded-lg shadow p-4">
249
+ <div class="text-sm text-gray-500">Team</div>
250
+ <div><%= owner.team %></div>
251
+ </div>
252
+ <% } %>
253
+ </div>
254
+
255
+ <% if (ownedNodes.length > 0) { %>
256
+ <h2 class="text-xl font-semibold mb-3">Owned Nodes</h2>
257
+ <ul class="space-y-1">
258
+ <% ownedNodes.forEach(function(node) { %>
259
+ <li>
260
+ <span class="inline-block bg-gray-100 text-xs px-1.5 py-0.5 rounded mr-1"><%= node.kind %></span>
261
+ <a href="<%= node.href %>" class="text-blue-600 hover:underline"><%= node.id %></a>
262
+ </li>
263
+ <% }); %>
264
+ </ul>
265
+ <% } %>`;
266
+ var glossaryTemplate = `<nav class="text-sm text-gray-500 mb-4">
267
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Glossary
268
+ </nav>
269
+
270
+ <h1 class="text-3xl font-bold mb-6">Glossary</h1>
271
+
272
+ <% if (terms.length === 0) { %>
273
+ <p class="text-gray-500">No terms defined.</p>
274
+ <% } else { %>
275
+ <div class="overflow-x-auto">
276
+ <table class="min-w-full bg-white rounded-lg shadow text-sm">
277
+ <thead>
278
+ <tr class="bg-gray-50 text-left">
279
+ <th class="px-4 py-2">Term</th>
280
+ <th class="px-4 py-2">Definition</th>
281
+ <th class="px-4 py-2">Synonyms</th>
282
+ <th class="px-4 py-2">Maps To</th>
283
+ </tr>
284
+ </thead>
285
+ <tbody>
286
+ <% terms.forEach(function(term) { %>
287
+ <tr class="border-t">
288
+ <td class="px-4 py-2 font-medium"><%= term.id %></td>
289
+ <td class="px-4 py-2"><%= term.definition %></td>
290
+ <td class="px-4 py-2"><%= (term.synonyms || []).join(', ') %></td>
291
+ <td class="px-4 py-2">
292
+ <% (term.mapsTo || []).forEach(function(target) { %>
293
+ <a href="<%= basePath %>/concepts/<%= target %>.html" class="text-blue-600 hover:underline"><%= target %></a><%= ' ' %>
294
+ <% }); %>
295
+ </td>
296
+ </tr>
297
+ <% }); %>
298
+ </tbody>
299
+ </table>
300
+ </div>
301
+ <% } %>`;
302
+ var searchTemplate = `<nav class="text-sm text-gray-500 mb-4">
303
+ <a href="<%= basePath %>/" class="hover:text-blue-600">Home</a> &rsaquo; Search
304
+ </nav>
305
+
306
+ <h1 class="text-3xl font-bold mb-6">Search</h1>
307
+
308
+ <input
309
+ id="search-input"
310
+ type="text"
311
+ placeholder="Search concepts, products, policies, terms..."
312
+ 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"
313
+ >
314
+
315
+ <div id="search-results" class="space-y-3"></div>
316
+
317
+ <script>window.__CONTEXTKIT_BASE_PATH__ = '<%= basePath %>';</script>
318
+ <script src="<%= basePath %>/search.js"></script>`;
319
+
320
+ // src/search/build-index.ts
321
+ var _minisearch = require('minisearch'); var _minisearch2 = _interopRequireDefault(_minisearch);
322
+ function buildSearchIndex(manifest) {
323
+ const miniSearch = new (0, _minisearch2.default)({
324
+ fields: ["id", "text", "tags"],
325
+ storeFields: ["id", "kind", "text"],
326
+ searchOptions: {
327
+ boost: { id: 2 },
328
+ fuzzy: 0.2,
329
+ prefix: true
330
+ }
331
+ });
332
+ const documents = [];
333
+ for (const concept of manifest.concepts) {
334
+ documents.push({
335
+ id: concept.id,
336
+ kind: "concept",
337
+ text: concept.definition,
338
+ tags: (_nullishCoalesce(concept.tags, () => ( []))).join(" ")
339
+ });
340
+ }
341
+ for (const product of manifest.products) {
342
+ documents.push({
343
+ id: product.id,
344
+ kind: "product",
345
+ text: product.description,
346
+ tags: (_nullishCoalesce(product.tags, () => ( []))).join(" ")
347
+ });
348
+ }
349
+ for (const policy of manifest.policies) {
350
+ documents.push({
351
+ id: policy.id,
352
+ kind: "policy",
353
+ text: policy.description,
354
+ tags: (_nullishCoalesce(policy.tags, () => ( []))).join(" ")
355
+ });
356
+ }
357
+ for (const entity of manifest.entities) {
358
+ documents.push({
359
+ id: entity.id,
360
+ kind: "entity",
361
+ text: _nullishCoalesce(entity.definition, () => ( "")),
362
+ tags: (_nullishCoalesce(entity.tags, () => ( []))).join(" ")
363
+ });
364
+ }
365
+ for (const term of manifest.terms) {
366
+ documents.push({
367
+ id: term.id,
368
+ kind: "term",
369
+ text: term.definition,
370
+ tags: (_nullishCoalesce(term.tags, () => ( []))).join(" ")
371
+ });
372
+ }
373
+ miniSearch.addAll(documents);
374
+ return JSON.stringify(miniSearch);
375
+ }
376
+
377
+ // src/generator.ts
378
+ function renderPage(contentTemplate, data, pageTitle, manifest, basePath) {
379
+ const content = _ejs2.default.render(contentTemplate, { ...data, basePath });
380
+ return _ejs2.default.render(layoutTemplate, {
381
+ content,
382
+ pageTitle,
383
+ project: manifest.project,
384
+ build: manifest.build,
385
+ basePath
386
+ });
387
+ }
388
+ async function writeOutputFile(filePath, content) {
389
+ await _promises.mkdir.call(void 0, _path.dirname.call(void 0, filePath), { recursive: true });
390
+ await _promises.writeFile.call(void 0, filePath, content, "utf-8");
391
+ }
392
+ function resolveAssetPath(filename) {
393
+ const currentDir = _path.dirname.call(void 0, _url.fileURLToPath.call(void 0, import.meta.url));
394
+ return _path.join.call(void 0, currentDir, "assets", filename);
395
+ }
396
+ async function generateSite(options) {
397
+ const { manifest, outputDir, title, basePath = "" } = options;
398
+ const projectData = {
399
+ ...manifest.project,
400
+ displayName: _nullishCoalesce(title, () => ( manifest.project.displayName))
401
+ };
402
+ const manifestWithTitle = { ...manifest, project: projectData };
403
+ await _promises.mkdir.call(void 0, _path.join.call(void 0, outputDir, "concepts"), { recursive: true });
404
+ await _promises.mkdir.call(void 0, _path.join.call(void 0, outputDir, "products"), { recursive: true });
405
+ await _promises.mkdir.call(void 0, _path.join.call(void 0, outputDir, "policies"), { recursive: true });
406
+ await _promises.mkdir.call(void 0, _path.join.call(void 0, outputDir, "owners"), { recursive: true });
407
+ const indexHtml = renderPage(
408
+ indexTemplate,
409
+ {
410
+ project: manifestWithTitle.project,
411
+ concepts: manifest.concepts,
412
+ products: manifest.products,
413
+ policies: manifest.policies,
414
+ entities: manifest.entities,
415
+ terms: manifest.terms,
416
+ owners: manifest.owners
417
+ },
418
+ "Home",
419
+ manifestWithTitle,
420
+ basePath
421
+ );
422
+ await writeOutputFile(_path.join.call(void 0, outputDir, "index.html"), indexHtml);
423
+ for (const concept of manifest.concepts) {
424
+ const html = renderPage(
425
+ conceptTemplate,
426
+ { concept },
427
+ concept.id,
428
+ manifestWithTitle,
429
+ basePath
430
+ );
431
+ await writeOutputFile(_path.join.call(void 0, outputDir, "concepts", `${concept.id}.html`), html);
432
+ }
433
+ for (const product of manifest.products) {
434
+ const relatedConcepts = manifest.concepts.filter(
435
+ (c) => c.productId === product.id
436
+ );
437
+ const html = renderPage(
438
+ productTemplate,
439
+ { product, relatedConcepts },
440
+ product.id,
441
+ manifestWithTitle,
442
+ basePath
443
+ );
444
+ await writeOutputFile(_path.join.call(void 0, outputDir, "products", `${product.id}.html`), html);
445
+ }
446
+ for (const policy of manifest.policies) {
447
+ const html = renderPage(
448
+ policyTemplate,
449
+ { policy },
450
+ policy.id,
451
+ manifestWithTitle,
452
+ basePath
453
+ );
454
+ await writeOutputFile(_path.join.call(void 0, outputDir, "policies", `${policy.id}.html`), html);
455
+ }
456
+ for (const owner of manifest.owners) {
457
+ const ownedNodes = [];
458
+ for (const concept of manifest.concepts) {
459
+ if (concept.owner === owner.id) {
460
+ ownedNodes.push({
461
+ id: concept.id,
462
+ kind: "concept",
463
+ href: `${basePath}/concepts/${concept.id}.html`
464
+ });
465
+ }
466
+ }
467
+ for (const product of manifest.products) {
468
+ if (product.owner === owner.id) {
469
+ ownedNodes.push({
470
+ id: product.id,
471
+ kind: "product",
472
+ href: `${basePath}/products/${product.id}.html`
473
+ });
474
+ }
475
+ }
476
+ for (const policy of manifest.policies) {
477
+ if (policy.owner === owner.id) {
478
+ ownedNodes.push({
479
+ id: policy.id,
480
+ kind: "policy",
481
+ href: `${basePath}/policies/${policy.id}.html`
482
+ });
483
+ }
484
+ }
485
+ for (const entity of manifest.entities) {
486
+ if (entity.owner === owner.id) {
487
+ ownedNodes.push({
488
+ id: entity.id,
489
+ kind: "entity",
490
+ href: `${basePath}/`
491
+ });
492
+ }
493
+ }
494
+ for (const term of manifest.terms) {
495
+ if (term.owner === owner.id) {
496
+ ownedNodes.push({
497
+ id: term.id,
498
+ kind: "term",
499
+ href: `${basePath}/glossary.html`
500
+ });
501
+ }
502
+ }
503
+ const html = renderPage(
504
+ ownerTemplate,
505
+ { owner, ownedNodes },
506
+ owner.displayName,
507
+ manifestWithTitle,
508
+ basePath
509
+ );
510
+ await writeOutputFile(_path.join.call(void 0, outputDir, "owners", `${owner.id}.html`), html);
511
+ }
512
+ const glossaryHtml = renderPage(
513
+ glossaryTemplate,
514
+ { terms: manifest.terms },
515
+ "Glossary",
516
+ manifestWithTitle,
517
+ basePath
518
+ );
519
+ await writeOutputFile(_path.join.call(void 0, outputDir, "glossary.html"), glossaryHtml);
520
+ const searchHtml = renderPage(
521
+ searchTemplate,
522
+ {},
523
+ "Search",
524
+ manifestWithTitle,
525
+ basePath
526
+ );
527
+ await writeOutputFile(_path.join.call(void 0, outputDir, "search.html"), searchHtml);
528
+ const searchIndexJson = buildSearchIndex(manifest);
529
+ await writeOutputFile(_path.join.call(void 0, outputDir, "search-index.json"), searchIndexJson);
530
+ let searchJs;
531
+ try {
532
+ searchJs = await _promises.readFile.call(void 0, resolveAssetPath("search.js"), "utf-8");
533
+ } catch (e) {
534
+ searchJs = "// Search functionality - load search-index.json\n";
535
+ }
536
+ await writeOutputFile(_path.join.call(void 0, outputDir, "search.js"), searchJs);
537
+ let styleCss;
538
+ try {
539
+ styleCss = await _promises.readFile.call(void 0, resolveAssetPath("style.css"), "utf-8");
540
+ } catch (e2) {
541
+ styleCss = "/* ContextKit Site Styles */\n";
542
+ }
543
+ await writeOutputFile(_path.join.call(void 0, outputDir, "style.css"), styleCss);
544
+ }
545
+
546
+
547
+
548
+ exports.buildSearchIndex = buildSearchIndex; exports.generateSite = generateSite;
549
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/erickittelson/Desktop/ContextKit/packages/site/dist/index.cjs","../src/generator.ts","../src/templates.ts","../src/search/build-index.ts"],"names":[],"mappings":"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","file":"/Users/erickittelson/Desktop/ContextKit/packages/site/dist/index.cjs","sourcesContent":[null,"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> &middot; <%= 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> &rsaquo; Concepts &rsaquo; <%= 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> &rsaquo; Products &rsaquo; <%= 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> &mdash; <%= 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> &rsaquo; Policies &rsaquo; <%= 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> &rsaquo; Owners &rsaquo; <%= 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> &rsaquo; 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> &rsaquo; 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"]}
@@ -0,0 +1,24 @@
1
+ import { Manifest } from '@runcontext/core';
2
+
3
+ interface GenerateSiteOptions {
4
+ manifest: Manifest;
5
+ outputDir: string;
6
+ title?: string;
7
+ basePath?: string;
8
+ }
9
+ /**
10
+ * Generate a static documentation site from a ContextKit manifest.
11
+ *
12
+ * Creates HTML pages for all concepts, products, policies, owners,
13
+ * a glossary page, search page, and index page. Also generates a
14
+ * MiniSearch index for client-side search.
15
+ */
16
+ declare function generateSite(options: GenerateSiteOptions): Promise<void>;
17
+
18
+ /**
19
+ * Build a MiniSearch index from the manifest and return the serialized JSON string.
20
+ * The client-side search.js will load this JSON and use MiniSearch.loadJSON().
21
+ */
22
+ declare function buildSearchIndex(manifest: Manifest): string;
23
+
24
+ export { type GenerateSiteOptions, buildSearchIndex, generateSite };