@facet-coverage/core 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +64 -574
  2. package/dist/cli/commands/generate.d.ts.map +1 -1
  3. package/dist/cli/commands/generate.js +224 -25
  4. package/dist/cli/commands/generate.js.map +1 -1
  5. package/dist/cli/commands/serve.d.ts +12 -0
  6. package/dist/cli/commands/serve.d.ts.map +1 -0
  7. package/dist/cli/commands/serve.js +76 -0
  8. package/dist/cli/commands/serve.js.map +1 -0
  9. package/dist/cli/index.js +10 -0
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/client/index.js +60 -0
  12. package/dist/core/FacetParser.d.ts +37 -1
  13. package/dist/core/FacetParser.d.ts.map +1 -1
  14. package/dist/core/FacetParser.js +123 -5
  15. package/dist/core/FacetParser.js.map +1 -1
  16. package/dist/core/TestScanner.d.ts +4 -1
  17. package/dist/core/TestScanner.d.ts.map +1 -1
  18. package/dist/core/TestScanner.js +42 -13
  19. package/dist/core/TestScanner.js.map +1 -1
  20. package/dist/core/Validator.d.ts +4 -0
  21. package/dist/core/Validator.d.ts.map +1 -1
  22. package/dist/core/Validator.js +22 -4
  23. package/dist/core/Validator.js.map +1 -1
  24. package/dist/server/DevServer.d.ts +48 -0
  25. package/dist/server/DevServer.d.ts.map +1 -0
  26. package/dist/server/DevServer.js +126 -0
  27. package/dist/server/DevServer.js.map +1 -0
  28. package/dist/server/HotReloadManager.d.ts +49 -0
  29. package/dist/server/HotReloadManager.d.ts.map +1 -0
  30. package/dist/server/HotReloadManager.js +187 -0
  31. package/dist/server/HotReloadManager.js.map +1 -0
  32. package/dist/server/index.d.ts +3 -0
  33. package/dist/server/index.d.ts.map +1 -0
  34. package/dist/server/index.js +6 -0
  35. package/dist/server/index.js.map +1 -0
  36. package/dist/server/routes.d.ts +12 -0
  37. package/dist/server/routes.d.ts.map +1 -0
  38. package/dist/server/routes.js +272 -0
  39. package/dist/server/routes.js.map +1 -0
  40. package/dist/server/views/Dashboard.d.ts +6 -0
  41. package/dist/server/views/Dashboard.d.ts.map +1 -0
  42. package/dist/server/views/Dashboard.js +121 -0
  43. package/dist/server/views/Dashboard.js.map +1 -0
  44. package/dist/server/views/FacetPage.d.ts +6 -0
  45. package/dist/server/views/FacetPage.d.ts.map +1 -0
  46. package/dist/server/views/FacetPage.js +190 -0
  47. package/dist/server/views/FacetPage.js.map +1 -0
  48. package/dist/server/views/FeaturePage.d.ts +6 -0
  49. package/dist/server/views/FeaturePage.d.ts.map +1 -0
  50. package/dist/server/views/FeaturePage.js +98 -0
  51. package/dist/server/views/FeaturePage.js.map +1 -0
  52. package/dist/server/views/Layout.d.ts +17 -0
  53. package/dist/server/views/Layout.d.ts.map +1 -0
  54. package/dist/server/views/Layout.js +59 -0
  55. package/dist/server/views/Layout.js.map +1 -0
  56. package/dist/server/views/Sidebar.d.ts +6 -0
  57. package/dist/server/views/Sidebar.d.ts.map +1 -0
  58. package/dist/server/views/Sidebar.js +59 -0
  59. package/dist/server/views/Sidebar.js.map +1 -0
  60. package/dist/server/views/TestPanel.d.ts +6 -0
  61. package/dist/server/views/TestPanel.d.ts.map +1 -0
  62. package/dist/server/views/TestPanel.js +69 -0
  63. package/dist/server/views/TestPanel.js.map +1 -0
  64. package/dist/server/views/scripts.d.ts +5 -0
  65. package/dist/server/views/scripts.d.ts.map +1 -0
  66. package/dist/server/views/scripts.js +203 -0
  67. package/dist/server/views/scripts.js.map +1 -0
  68. package/dist/server/views/styles.d.ts +6 -0
  69. package/dist/server/views/styles.d.ts.map +1 -0
  70. package/dist/server/views/styles.js +608 -0
  71. package/dist/server/views/styles.js.map +1 -0
  72. package/dist/types.d.ts +27 -3
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js.map +1 -1
  75. package/package.json +9 -4
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderFeaturePage = renderFeaturePage;
4
+ const Layout_js_1 = require("./Layout.js");
5
+ const Sidebar_js_1 = require("./Sidebar.js");
6
+ /**
7
+ * Render a feature detail page
8
+ */
9
+ function renderFeaturePage(feature, report) {
10
+ const coverageClass = (0, Layout_js_1.getCoverageClass)(feature.percentage);
11
+ // Group facets by type
12
+ const facetsByType = {};
13
+ for (const fc of feature.facets) {
14
+ const type = fc.facet.type;
15
+ if (!facetsByType[type]) {
16
+ facetsByType[type] = [];
17
+ }
18
+ facetsByType[type].push(fc);
19
+ }
20
+ // Type sections
21
+ const typeSectionsHtml = Object.entries(facetsByType)
22
+ .map(([type, facets]) => {
23
+ const covered = facets.filter(fc => fc.covered).length;
24
+ const total = facets.length;
25
+ const typePercentage = Math.round((covered / total) * 100);
26
+ const typeClass = (0, Layout_js_1.getCoverageClass)(typePercentage);
27
+ const facetsHtml = facets
28
+ .map(fc => {
29
+ const statusClass = fc.covered ? 'covered' : 'uncovered';
30
+ const testsCount = fc.coveredBy.length;
31
+ return `
32
+ <a href="/facet/${encodeURIComponent(fc.facet.id)}" class="facet-item ${statusClass}">
33
+ <div class="facet-header">
34
+ <span class="facet-id">${fc.covered ? '&#10003;' : '&#10007;'} ${(0, Layout_js_1.escapeHtml)(fc.facet.id)}</span>
35
+ ${testsCount > 0 ? `<span class="badge success">${testsCount} test${testsCount !== 1 ? 's' : ''}</span>` : ''}
36
+ </div>
37
+ <div class="facet-source">${(0, Layout_js_1.escapeHtml)(fc.facet.source.file)}#${(0, Layout_js_1.escapeHtml)(fc.facet.source.section)}</div>
38
+ </a>
39
+ `;
40
+ })
41
+ .join('');
42
+ return `
43
+ <div class="card">
44
+ <h2 style="display: flex; justify-content: space-between; align-items: center;">
45
+ <span style="text-transform: capitalize;">${(0, Layout_js_1.escapeHtml)(type)}</span>
46
+ <span class="badge ${typeClass}">${typePercentage}% (${covered}/${total})</span>
47
+ </h2>
48
+ <div class="facet-list">
49
+ ${facetsHtml}
50
+ </div>
51
+ </div>
52
+ `;
53
+ })
54
+ .join('');
55
+ return `
56
+ <div class="app">
57
+ ${(0, Sidebar_js_1.renderSidebar)(report, feature.feature)}
58
+
59
+ <main class="main">
60
+ <div class="doc-panel">
61
+ <div class="breadcrumb">
62
+ <a href="/">Dashboard</a>
63
+ <span class="breadcrumb-separator">/</span>
64
+ <span>${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
65
+ </div>
66
+
67
+ <div class="card">
68
+ <h2 style="display: flex; justify-content: space-between; align-items: center;">
69
+ <span>${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
70
+ <span class="badge ${coverageClass}">${feature.percentage}%</span>
71
+ </h2>
72
+ <div class="stats-grid" style="margin-top: 1rem;">
73
+ <div class="stat">
74
+ <div class="stat-value ${coverageClass}">${feature.percentage}%</div>
75
+ <div class="stat-label">Coverage</div>
76
+ </div>
77
+ <div class="stat">
78
+ <div class="stat-value">${feature.totalFacets}</div>
79
+ <div class="stat-label">Total Facets</div>
80
+ </div>
81
+ <div class="stat">
82
+ <div class="stat-value success">${feature.coveredFacets}</div>
83
+ <div class="stat-label">Covered</div>
84
+ </div>
85
+ <div class="stat">
86
+ <div class="stat-value error">${feature.totalFacets - feature.coveredFacets}</div>
87
+ <div class="stat-label">Uncovered</div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ ${typeSectionsHtml}
93
+ </div>
94
+ </main>
95
+ </div>
96
+ `;
97
+ }
98
+ //# sourceMappingURL=FeaturePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FeaturePage.js","sourceRoot":"","sources":["../../../src/server/views/FeaturePage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ interface LayoutOptions {
2
+ title?: string;
3
+ }
4
+ /**
5
+ * Render the main HTML layout shell
6
+ */
7
+ export declare function renderLayout(content: string, options?: LayoutOptions): string;
8
+ /**
9
+ * Escape HTML special characters
10
+ */
11
+ export declare function escapeHtml(text: string): string;
12
+ /**
13
+ * Get CSS class for coverage percentage
14
+ */
15
+ export declare function getCoverageClass(percentage: number): string;
16
+ export {};
17
+ //# sourceMappingURL=Layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layout.d.ts","sourceRoot":"","sources":["../../../src/server/views/Layout.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CA2BjF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI3D"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderLayout = renderLayout;
4
+ exports.escapeHtml = escapeHtml;
5
+ exports.getCoverageClass = getCoverageClass;
6
+ const styles_js_1 = require("./styles.js");
7
+ const scripts_js_1 = require("./scripts.js");
8
+ /**
9
+ * Render the main HTML layout shell
10
+ */
11
+ function renderLayout(content, options = {}) {
12
+ const title = options.title || 'Facet Documentation';
13
+ return `<!DOCTYPE html>
14
+ <html lang="en">
15
+ <head>
16
+ <meta charset="UTF-8">
17
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
18
+ <title>${escapeHtml(title)}</title>
19
+ <style>
20
+ ${(0, styles_js_1.getStyles)()}
21
+ </style>
22
+ </head>
23
+ <body>
24
+ ${content}
25
+
26
+ <!-- Status indicator for hot reload -->
27
+ <div id="status-indicator" class="status-indicator">
28
+ <div class="spinner"></div>
29
+ <span id="status-text">Connecting...</span>
30
+ </div>
31
+
32
+ <script>
33
+ ${(0, scripts_js_1.getScripts)()}
34
+ </script>
35
+ </body>
36
+ </html>`;
37
+ }
38
+ /**
39
+ * Escape HTML special characters
40
+ */
41
+ function escapeHtml(text) {
42
+ return text
43
+ .replace(/&/g, '&amp;')
44
+ .replace(/</g, '&lt;')
45
+ .replace(/>/g, '&gt;')
46
+ .replace(/"/g, '&quot;')
47
+ .replace(/'/g, '&#039;');
48
+ }
49
+ /**
50
+ * Get CSS class for coverage percentage
51
+ */
52
+ function getCoverageClass(percentage) {
53
+ if (percentage >= 80)
54
+ return 'success';
55
+ if (percentage >= 50)
56
+ return 'warning';
57
+ return 'error';
58
+ }
59
+ //# sourceMappingURL=Layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Layout.js","sourceRoot":"","sources":["../../../src/server/views/Layout.ts"],"names":[],"mappings":";;AAUA,oCA2BC;AAKD,gCAOC;AAKD,4CAIC;AA1DD,2CAAwC;AACxC,6CAA0C;AAM1C;;GAEG;AACH,SAAgB,YAAY,CAAC,OAAe,EAAE,UAAyB,EAAE;IACvE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,qBAAqB,CAAC;IAErD,OAAO;;;;;WAKE,UAAU,CAAC,KAAK,CAAC;;MAEtB,IAAA,qBAAS,GAAE;;;;IAIb,OAAO;;;;;;;;;MASL,IAAA,uBAAU,GAAE;;;QAGV,CAAC;AACT,CAAC;AAED;;GAEG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IACvC,IAAI,UAAU,IAAI,EAAE;QAAE,OAAO,SAAS,CAAC;IACvC,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { CoverageReport } from '../../types.js';
2
+ /**
3
+ * Render the sidebar navigation
4
+ */
5
+ export declare function renderSidebar(report: CoverageReport, activeFeature?: string, activeFacet?: string): string;
6
+ //# sourceMappingURL=Sidebar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sidebar.d.ts","sourceRoot":"","sources":["../../../src/server/views/Sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAqD1G"}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderSidebar = renderSidebar;
4
+ const Layout_js_1 = require("./Layout.js");
5
+ /**
6
+ * Render the sidebar navigation
7
+ */
8
+ function renderSidebar(report, activeFeature, activeFacet) {
9
+ const featuresHtml = report.features
10
+ .map(feature => {
11
+ const coverageClass = (0, Layout_js_1.getCoverageClass)(feature.percentage);
12
+ const isActive = feature.feature === activeFeature;
13
+ const facetsHtml = feature.facets
14
+ .map(fc => {
15
+ const facetActive = fc.facet.id === activeFacet;
16
+ const coverStatus = fc.covered ? 'covered' : 'uncovered';
17
+ return `
18
+ <a href="/facet/${encodeURIComponent(fc.facet.id)}"
19
+ class="nav-item ${coverStatus}${facetActive ? ' active' : ''}"
20
+ title="${(0, Layout_js_1.escapeHtml)(fc.facet.id)}">
21
+ ${(0, Layout_js_1.escapeHtml)(fc.facet.id.split(':').pop() || fc.facet.id)}
22
+ </a>
23
+ `;
24
+ })
25
+ .join('');
26
+ return `
27
+ <div class="nav-section">
28
+ <a href="/feature/${encodeURIComponent(feature.feature)}" class="nav-section-header">
29
+ <span class="nav-section-title">${(0, Layout_js_1.escapeHtml)(feature.feature)}</span>
30
+ <span class="nav-section-badge ${coverageClass}">${feature.percentage}%</span>
31
+ </a>
32
+ <div class="nav-items">
33
+ ${facetsHtml}
34
+ </div>
35
+ </div>
36
+ `;
37
+ })
38
+ .join('');
39
+ return `
40
+ <aside class="sidebar">
41
+ <div class="sidebar-header">
42
+ <h1>Facet Docs</h1>
43
+ </div>
44
+ <div class="sidebar-search">
45
+ <input type="text" id="sidebar-search" placeholder="Search facets... (Ctrl+K)" />
46
+ </div>
47
+ <nav class="sidebar-nav">
48
+ <div class="nav-section">
49
+ <a href="/" class="nav-section-header">
50
+ <span class="nav-section-title">Dashboard</span>
51
+ <span class="nav-section-badge ${(0, Layout_js_1.getCoverageClass)(report.summary.percentage)}">${report.summary.percentage}%</span>
52
+ </a>
53
+ </div>
54
+ ${featuresHtml}
55
+ </nav>
56
+ </aside>
57
+ `;
58
+ }
59
+ //# sourceMappingURL=Sidebar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["../../../src/server/views/Sidebar.ts"],"names":[],"mappings":";;AAMA,sCAqDC;AA1DD,2CAA2D;AAE3D;;GAEG;AACH,SAAgB,aAAa,CAAC,MAAsB,EAAE,aAAsB,EAAE,WAAoB;IAChG,MAAM,YAAY,GAAG,MAAM,CAAC,QAAQ;SACjC,GAAG,CAAC,OAAO,CAAC,EAAE;QACb,MAAM,aAAa,GAAG,IAAA,4BAAgB,EAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,KAAK,aAAa,CAAC;QAEnD,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM;aAC9B,GAAG,CAAC,EAAE,CAAC,EAAE;YACR,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,WAAW,CAAC;YAChD,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;YACzD,OAAO;8BACa,kBAAkB,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;iCAC5B,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;wBACnD,IAAA,sBAAU,EAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,IAAA,sBAAU,EAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;;WAE5D,CAAC;QACJ,CAAC,CAAC;aACD,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO;;8BAEiB,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC;8CACnB,IAAA,sBAAU,EAAC,OAAO,CAAC,OAAO,CAAC;6CAC5B,aAAa,KAAK,OAAO,CAAC,UAAU;;;cAGnE,UAAU;;;OAGjB,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;;;;;;;;;;;6CAYoC,IAAA,4BAAgB,EAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,OAAO,CAAC,UAAU;;;UAG5G,YAAY;;;GAGnB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { TestLink } from '../../types.js';
2
+ /**
3
+ * Render the test panel showing tests that cover a facet
4
+ */
5
+ export declare function renderTestPanel(tests: TestLink[]): string;
6
+ //# sourceMappingURL=TestPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestPanel.d.ts","sourceRoot":"","sources":["../../../src/server/views/TestPanel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG/C;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CA4CzD"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderTestPanel = renderTestPanel;
4
+ const Layout_js_1 = require("./Layout.js");
5
+ /**
6
+ * Render the test panel showing tests that cover a facet
7
+ */
8
+ function renderTestPanel(tests) {
9
+ if (tests.length === 0) {
10
+ return `
11
+ <aside class="test-panel">
12
+ <div class="test-panel-header">Tests (0)</div>
13
+ <div class="test-panel-content">
14
+ <div class="empty-state">
15
+ <div class="empty-state-icon">&#128269;</div>
16
+ <p>No tests cover this facet</p>
17
+ <p style="margin-top: 0.5rem; font-size: 0.8125rem;">
18
+ Add a <code>facet()</code> call in your tests to link them.
19
+ </p>
20
+ </div>
21
+ </div>
22
+ </aside>
23
+ `;
24
+ }
25
+ const testsHtml = tests
26
+ .map((test, index) => `
27
+ <div class="test-item${index === 0 ? ' active' : ''}"
28
+ data-file="${(0, Layout_js_1.escapeHtml)(test.file)}"
29
+ data-line="${test.line || ''}">
30
+ <div class="test-file">${(0, Layout_js_1.escapeHtml)(test.file)}</div>
31
+ <div class="test-title">${(0, Layout_js_1.escapeHtml)(test.title)}</div>
32
+ ${test.line ? `<div class="test-line">Line ${test.line}</div>` : ''}
33
+ </div>
34
+ `)
35
+ .join('');
36
+ return `
37
+ <aside class="test-panel">
38
+ <div class="test-panel-header">Tests (${tests.length})</div>
39
+ <div class="test-panel-content">
40
+ <div class="test-list">
41
+ ${testsHtml}
42
+ </div>
43
+
44
+ <div id="test-code" style="margin-top: 1rem;">
45
+ ${tests.length > 0 ? renderInitialTestCode(tests[0]) : ''}
46
+ </div>
47
+ </div>
48
+ </aside>
49
+ `;
50
+ }
51
+ /**
52
+ * Render placeholder for initial test code (will be loaded via JS)
53
+ */
54
+ function renderInitialTestCode(test) {
55
+ return `
56
+ <div class="code-block">
57
+ <div class="code-header">
58
+ <span>${(0, Layout_js_1.escapeHtml)(test.file)}</span>
59
+ <button class="copy-btn" onclick="copyToClipboard('bun test ${(0, Layout_js_1.escapeHtml)(test.file)}')">Copy run command</button>
60
+ </div>
61
+ <div class="code-content">
62
+ <div class="empty-state" style="padding: 1rem;">
63
+ Click a test to view source code
64
+ </div>
65
+ </div>
66
+ </div>
67
+ `;
68
+ }
69
+ //# sourceMappingURL=TestPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestPanel.js","sourceRoot":"","sources":["../../../src/server/views/TestPanel.ts"],"names":[],"mappings":";;AAMA,0CA4CC;AAjDD,2CAAyC;AAEzC;;GAEG;AACH,SAAgB,eAAe,CAAC,KAAiB;IAC/C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO;;;;;;;;;;;;;KAaN,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,KAAK;SACpB,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;6BACG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;wBACjC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;wBACrB,IAAI,CAAC,IAAI,IAAI,EAAE;iCACN,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;kCACpB,IAAA,sBAAU,EAAC,IAAI,CAAC,KAAK,CAAC;UAC9C,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,+BAA+B,IAAI,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;;KAEtE,CAAC;SACD,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO;;8CAEqC,KAAK,CAAC,MAAM;;;YAG9C,SAAS;;;;YAIT,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;;;;GAIhE,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAc;IAC3C,OAAO;;;gBAGO,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;sEACiC,IAAA,sBAAU,EAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;GAQxF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Client-side JavaScript for hot reload and interactivity
3
+ */
4
+ export declare function getScripts(): string;
5
+ //# sourceMappingURL=scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../../../src/server/views/scripts.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAmMnC"}
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getScripts = getScripts;
4
+ /**
5
+ * Client-side JavaScript for hot reload and interactivity
6
+ */
7
+ function getScripts() {
8
+ return `
9
+ (function() {
10
+ // WebSocket connection for hot reload
11
+ let ws = null;
12
+ let reconnectAttempts = 0;
13
+ const maxReconnectAttempts = 10;
14
+ const reconnectDelay = 1000;
15
+
16
+ const statusIndicator = document.getElementById('status-indicator');
17
+ const statusText = document.getElementById('status-text');
18
+
19
+ function showStatus(message, type) {
20
+ if (!statusIndicator) return;
21
+ statusIndicator.className = 'status-indicator visible ' + type;
22
+ if (statusText) statusText.textContent = message;
23
+ }
24
+
25
+ function hideStatus() {
26
+ if (statusIndicator) statusIndicator.className = 'status-indicator';
27
+ }
28
+
29
+ function connect() {
30
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
31
+ ws = new WebSocket(protocol + '//' + window.location.host + '/ws');
32
+
33
+ ws.onopen = function() {
34
+ console.log('[Facet] WebSocket connected');
35
+ reconnectAttempts = 0;
36
+ showStatus('Connected', 'connected');
37
+ setTimeout(hideStatus, 2000);
38
+ };
39
+
40
+ ws.onclose = function() {
41
+ console.log('[Facet] WebSocket disconnected');
42
+ ws = null;
43
+
44
+ if (reconnectAttempts < maxReconnectAttempts) {
45
+ reconnectAttempts++;
46
+ const delay = reconnectDelay * Math.min(reconnectAttempts, 5);
47
+ console.log('[Facet] Reconnecting in ' + delay + 'ms...');
48
+ setTimeout(connect, delay);
49
+ }
50
+ };
51
+
52
+ ws.onerror = function(error) {
53
+ console.error('[Facet] WebSocket error:', error);
54
+ };
55
+
56
+ ws.onmessage = function(event) {
57
+ try {
58
+ const message = JSON.parse(event.data);
59
+ handleMessage(message);
60
+ } catch (e) {
61
+ console.error('[Facet] Failed to parse message:', e);
62
+ }
63
+ };
64
+ }
65
+
66
+ function handleMessage(message) {
67
+ switch (message.type) {
68
+ case 'connected':
69
+ console.log('[Facet] Server acknowledged connection');
70
+ break;
71
+
72
+ case 'file-change':
73
+ showStatus('File changed: ' + message.data.path, 'updating');
74
+ break;
75
+
76
+ case 'coverage-update':
77
+ console.log('[Facet] Coverage updated:', message.data.summary);
78
+ showStatus('Refreshing...', 'updating');
79
+ // Reload the page to show new coverage
80
+ setTimeout(function() {
81
+ window.location.reload();
82
+ }, 300);
83
+ break;
84
+
85
+ case 'error':
86
+ console.error('[Facet] Server error:', message.data.message);
87
+ showStatus('Error: ' + message.data.message, 'error');
88
+ break;
89
+ }
90
+ }
91
+
92
+ // Start WebSocket connection
93
+ connect();
94
+
95
+ // Search functionality
96
+ const searchInput = document.getElementById('sidebar-search');
97
+ if (searchInput) {
98
+ searchInput.addEventListener('input', function(e) {
99
+ const query = e.target.value.toLowerCase();
100
+ const items = document.querySelectorAll('.nav-item');
101
+
102
+ items.forEach(function(item) {
103
+ const text = item.textContent.toLowerCase();
104
+ const section = item.closest('.nav-section');
105
+
106
+ if (query === '' || text.includes(query)) {
107
+ item.style.display = '';
108
+ } else {
109
+ item.style.display = 'none';
110
+ }
111
+ });
112
+
113
+ // Show/hide sections based on visible items
114
+ document.querySelectorAll('.nav-section').forEach(function(section) {
115
+ const visibleItems = section.querySelectorAll('.nav-item:not([style*="display: none"])');
116
+ section.style.display = visibleItems.length > 0 || query === '' ? '' : 'none';
117
+ });
118
+ });
119
+ }
120
+
121
+ // Test panel - load test source on click
122
+ document.querySelectorAll('.test-item').forEach(function(item) {
123
+ item.addEventListener('click', function() {
124
+ const file = item.dataset.file;
125
+ const line = item.dataset.line;
126
+
127
+ if (!file) return;
128
+
129
+ // Mark as active
130
+ document.querySelectorAll('.test-item').forEach(function(i) {
131
+ i.classList.remove('active');
132
+ });
133
+ item.classList.add('active');
134
+
135
+ // Load test source
136
+ loadTestSource(file, line);
137
+ });
138
+ });
139
+
140
+ async function loadTestSource(file, line) {
141
+ const codeContainer = document.getElementById('test-code');
142
+ if (!codeContainer) return;
143
+
144
+ codeContainer.innerHTML = '<div class="empty-state">Loading...</div>';
145
+
146
+ try {
147
+ const response = await fetch('/api/test-source?file=' + encodeURIComponent(file) + (line ? '&line=' + line : ''));
148
+ const data = await response.json();
149
+
150
+ if (data.error) {
151
+ codeContainer.innerHTML = '<div class="empty-state">' + data.error + '</div>';
152
+ return;
153
+ }
154
+
155
+ const lines = data.content.split('\\n');
156
+ let html = '<div class="code-block">';
157
+ html += '<div class="code-header"><span>' + data.file + '</span>';
158
+ html += '<button class="copy-btn" onclick="copyToClipboard(\\'bun test ' + data.file + '\\')">Copy run command</button>';
159
+ html += '</div>';
160
+ html += '<div class="code-content">';
161
+
162
+ lines.forEach(function(lineContent, index) {
163
+ const lineNum = data.startLine + index;
164
+ const isHighlight = lineNum === data.highlightLine;
165
+ html += '<div class="code-line' + (isHighlight ? ' highlight' : '') + '">';
166
+ html += '<span class="code-line-number">' + lineNum + '</span>';
167
+ html += '<span class="code-line-content">' + escapeHtml(lineContent) + '</span>';
168
+ html += '</div>';
169
+ });
170
+
171
+ html += '</div></div>';
172
+ codeContainer.innerHTML = html;
173
+ } catch (e) {
174
+ codeContainer.innerHTML = '<div class="empty-state">Failed to load source</div>';
175
+ }
176
+ }
177
+
178
+ function escapeHtml(text) {
179
+ const div = document.createElement('div');
180
+ div.textContent = text;
181
+ return div.innerHTML;
182
+ }
183
+
184
+ // Copy to clipboard helper
185
+ window.copyToClipboard = function(text) {
186
+ navigator.clipboard.writeText(text).then(function() {
187
+ // Could show a toast here
188
+ console.log('[Facet] Copied to clipboard');
189
+ });
190
+ };
191
+
192
+ // Keyboard shortcuts
193
+ document.addEventListener('keydown', function(e) {
194
+ // Ctrl/Cmd + K to focus search
195
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
196
+ e.preventDefault();
197
+ if (searchInput) searchInput.focus();
198
+ }
199
+ });
200
+ })();
201
+ `;
202
+ }
203
+ //# sourceMappingURL=scripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../../src/server/views/scripts.ts"],"names":[],"mappings":";;AAGA,gCAmMC;AAtMD;;GAEG;AACH,SAAgB,UAAU;IACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiMN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * CSS styles for the documentation server
3
+ * Based on the HtmlReporter design system
4
+ */
5
+ export declare function getStyles(): string;
6
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../src/server/views/styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAulBlC"}