spec-up-t 1.4.1-beta.3 → 1.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.
@@ -1,651 +1,69 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * @fileoverview Spec-Up-T Health Check Tool
4
+ * @fileoverview Spec-Up-T Health Check Tool - Wrapper for spec-up-t-healthcheck
5
5
  *
6
- * This script performs comprehensive health checks on Spec-Up-T projects,
7
- * validating configuration, external references, term definitions, and more.
8
- * Generates an HTML report with detailed results and actionable feedback.
6
+ * This script is called from consuming projects via:
7
+ * require('spec-up-t/src/health-check.js')
8
+ *
9
+ * It delegates to the new spec-up-t-healthcheck npm package while maintaining
10
+ * backward compatibility with existing consuming projects.
9
11
  *
10
12
  * @author Spec-Up-T Team
11
- * @version 1.0.0
12
- * @since 2025-06-06
13
- */
14
-
15
- /**
16
- * @typedef {Object} HealthCheckResult
17
- * @property {string} name - Name of the specific check
18
- * @property {boolean|string} success - Success status (true, false, or 'partial')
19
- * @property {string} [status] - Status override ('warning', 'pass', 'fail')
20
- * @property {string} [details] - Additional details about the check result
21
- */
22
-
23
- /**
24
- * @typedef {Object} HealthCheckSection
25
- * @property {string} title - Title of the check section
26
- * @property {HealthCheckResult[]} results - Array of individual check results
27
- */
28
-
29
- /**
30
- * @typedef {Object} RepositoryInfo
31
- * @property {string} host - Git hosting service (e.g., 'github')
32
- * @property {string} account - Account/organization name
33
- * @property {string} repo - Repository name
34
- * @property {string} [branch] - Branch name
35
- * @property {boolean} [verified] - Whether repository existence was verified
36
- */
37
-
38
- /**
39
- * @typedef {Object} StatusDisplay
40
- * @property {string} statusClass - CSS class for styling
41
- * @property {string} statusIcon - HTML icon element
42
- * @property {string} statusText - Display text for status
13
+ * @version 2.0.0
14
+ * @since 2025-10-11
15
+ * @deprecated The old implementation is replaced by spec-up-t-healthcheck
43
16
  */
44
17
 
45
- const fs = require('fs');
46
18
  const path = require('path');
47
- const Logger = require('./utils/logger');
48
- const https = require('https');
49
- const fileOpener = require('./utils/file-opener');
50
-
51
- // Import modules from the health-check directory
52
- const externalSpecsChecker = require('./health-check/external-specs-checker');
53
- const termReferencesChecker = require('./health-check/term-references-checker');
54
- const specsConfigurationChecker = require('./health-check/specs-configuration-checker');
55
- const termsIntroChecker = require('./health-check/terms-intro-checker');
56
- const destinationGitignoreChecker = require('./health-check/destination-gitignore-checker');
57
- const trefTermChecker = require('./health-check/tref-term-checker');
19
+ const {spawn} = require('child_process');
58
20
 
59
21
  /**
60
- * Directory where health check reports are generated
61
- * @constant {string}
62
- */
63
- const OUTPUT_DIR = path.join(process.cwd(), '.cache');
64
-
65
- // Create output directory if it doesn't exist
66
- if (!fs.existsSync(OUTPUT_DIR)) {
67
- fs.mkdirSync(OUTPUT_DIR, { recursive: true });
68
- }
69
-
70
- /**
71
- * Retrieves repository information from specs.json file
72
- *
73
- * Reads the specs.json file to extract repository configuration including
74
- * host, account, repo name, and branch. Falls back to default values if
75
- * the file doesn't exist or is missing required fields.
22
+ * Main function that runs the health check on the consuming project
76
23
  *
77
- * @async
78
- * @function getRepoInfo
79
- * @returns {Promise<RepositoryInfo>} Repository information object
24
+ * This function is called when a consuming project executes:
25
+ * npm run healthCheck
80
26
  *
81
- * @example
82
- * const repoInfo = await getRepoInfo();
83
- * console.log(repoInfo.account); // 'blockchain-bird'
27
+ * Which triggers: require('spec-up-t/src/health-check.js')
84
28
  */
85
- async function getRepoInfo() {
86
- try {
87
- // Path to the default boilerplate specs.json
88
- const defaultSpecsPath = path.join(
89
- __dirname,
90
- 'install-from-boilerplate',
91
- 'boilerplate',
92
- 'specs.json'
93
- );
94
-
95
- let defaultValues = {
96
- host: 'github',
97
- account: 'blockchain-bird',
98
- repo: 'spec-up-t'
99
- };
100
-
101
- // Try to load default values from boilerplate specs.json
102
- if (fs.existsSync(defaultSpecsPath)) {
103
- try {
104
- const defaultSpecsContent = fs.readFileSync(defaultSpecsPath, 'utf8');
105
- const defaultSpecs = JSON.parse(defaultSpecsContent);
106
-
107
- if (defaultSpecs?.specs?.[0]?.source) {
108
- const source = defaultSpecs.specs[0].source;
109
- defaultValues = {
110
- host: source.host || defaultValues.host,
111
- account: source.account || defaultValues.account,
112
- repo: source.repo || defaultValues.repo
113
- };
114
- }
115
- } catch (error) {
116
- Logger.error('Error reading boilerplate specs.json:', error);
117
- }
118
- }
119
-
120
- // Look for specs.json in the current working directory (where the command is run from)
121
- const specsPath = path.join(process.cwd(), 'specs.json');
122
-
123
- if (fs.existsSync(specsPath)) {
124
- const specsContent = fs.readFileSync(specsPath, 'utf8');
125
- const specs = JSON.parse(specsContent);
126
-
127
- // Check if source field exists and has required properties
128
- if (specs?.specs?.[0]?.source?.host &&
129
- specs?.specs?.[0]?.source?.account &&
130
- specs?.specs?.[0]?.source?.repo) {
131
-
132
- const sourceInfo = specs.specs[0].source;
133
-
134
- // Check if values have been changed from defaults
135
- const valuesChanged =
136
- sourceInfo.host !== defaultValues.host ||
137
- sourceInfo.account !== defaultValues.account ||
138
- sourceInfo.repo !== defaultValues.repo;
139
-
140
- // If values haven't changed, just return the default values
141
- // This allows the user to manually change these later
142
- if (!valuesChanged) {
143
- return {
144
- host: sourceInfo.host,
145
- account: sourceInfo.account,
146
- repo: sourceInfo.repo,
147
- branch: sourceInfo.branch || defaultValues.branch
148
- };
149
- }
150
-
151
- // If values have changed, verify the repository exists
152
- const repoExists = await checkRepositoryExists(
153
- sourceInfo.host,
154
- sourceInfo.account,
155
- sourceInfo.repo
156
- );
157
-
158
- if (repoExists) {
159
- // Repository exists, return the verified info
160
- return {
161
- host: sourceInfo.host,
162
- account: sourceInfo.account,
163
- repo: sourceInfo.repo,
164
- branch: sourceInfo.branch || defaultValues.branch,
165
- verified: true
166
- };
167
- } else {
168
- // Repository doesn't exist, but values have been changed
169
- // Return the values with a verification failed flag
170
- return {
171
- host: sourceInfo.host,
172
- account: sourceInfo.account,
173
- repo: sourceInfo.repo,
174
- branch: sourceInfo.branch || defaultValues.branch,
175
- verified: false
176
- };
177
- }
178
- }
179
- } else {
180
- Logger.info('specs.json not found');
181
- }
182
- } catch (error) {
183
- Logger.error('Error reading specs.json:', error);
184
- }
29
+ async function runHealthCheck() {
30
+ console.log('🏥 Initializing Spec-Up-T Health Check...\n');
185
31
 
186
- // Return default values if specs.json doesn't exist or doesn't contain the required information
187
- return {
188
- host: 'github',
189
- account: 'blockchain-bird',
190
- repo: 'spec-up-t',
191
- branch: 'main'
192
- };
193
- }
194
-
195
- /**
196
- * Checks if a Git repository exists and is accessible
197
- *
198
- * Makes an HTTP HEAD request to verify repository existence without
199
- * downloading the full repository content. Handles timeouts and errors gracefully.
200
- *
201
- * @function checkRepositoryExists
202
- * @param {string} host - Git hosting service (e.g., 'github')
203
- * @param {string} account - Account or organization name
204
- * @param {string} repo - Repository name
205
- * @returns {Promise<boolean>} True if repository exists and is accessible
206
- *
207
- * @example
208
- * const exists = await checkRepositoryExists('github', 'blockchain-bird', 'spec-up-t');
209
- * if (exists) {
210
- * console.log('Repository is accessible');
211
- * }
212
- */
213
- function checkRepositoryExists(host, account, repo) {
214
- return new Promise((resolve) => {
215
- const url = `https://${host}.com/${account}/${repo}`;
216
-
217
- try {
218
- const request = https.request(url, { method: 'HEAD', timeout: 10000 }, (response) => {
219
- // 200, 301, 302 status codes indicate the repo exists
220
- const exists = [200, 301, 302].includes(response.statusCode);
221
- resolve(exists);
222
- });
223
-
224
- request.on('error', (error) => {
225
- Logger.error('Error checking repository existence:', error.message);
226
- resolve(false);
227
- });
228
-
229
- request.on('timeout', () => {
230
- Logger.error('Timeout checking repository existence');
231
- request.destroy();
232
- resolve(false);
233
- });
234
-
235
- request.end();
236
- } catch (error) {
237
- Logger.error('Error checking repository existence:', error.message);
238
- resolve(false);
239
- }
32
+ // Get the path to the run-healthcheck.js script in spec-up-t
33
+ const healthCheckScript = path.join(__dirname, 'run-healthcheck.js');
34
+
35
+ // Pass through all command line arguments (excluding node and script name)
36
+ const args = process.argv.slice(2);
37
+
38
+ // Run the health check script with Node, passing through all arguments
39
+ // The script will automatically check process.cwd() (the consuming project)
40
+ const child = spawn('node', [healthCheckScript, ...args], {
41
+ stdio: 'inherit', // Pass through all I/O
42
+ cwd: process.cwd() // Ensure we're in the consuming project directory
240
43
  });
241
- }
242
-
243
- /**
244
- * Formats current timestamp for use in filenames
245
- *
246
- * Generates a timestamp string that is safe to use in filenames by
247
- * replacing special characters with hyphens. Format: YYYY-MM-DD-HH-mm-ssZ
248
- *
249
- * @function getFormattedTimestamp
250
- * @returns {string} Formatted timestamp string suitable for filenames
251
- *
252
- * @example
253
- * const timestamp = getFormattedTimestamp();
254
- * console.log(timestamp); // "2025-06-06-14-30-25Z"
255
- */
256
- function getFormattedTimestamp() {
257
- const now = new Date();
258
- return now.toISOString()
259
- .replace(/[T:]/g, '-')
260
- .replace(/\..+/, '')
261
- .replace(/Z/g, 'Z');
262
- }
263
-
264
- /**
265
- * Generates a human-readable timestamp for display in reports
266
- *
267
- * Creates a localized timestamp string for display purposes,
268
- * using the system's default locale and timezone.
269
- *
270
- * @function getHumanReadableTimestamp
271
- * @returns {string} Human-readable timestamp string
272
- *
273
- * @example
274
- * const readable = getHumanReadableTimestamp();
275
- * console.log(readable); // "6/6/2025, 2:30:25 PM"
276
- */
277
- function getHumanReadableTimestamp() {
278
- return new Date().toLocaleString();
279
- }
280
-
281
- /**
282
- * Determines status display parameters based on check result
283
- *
284
- * Analyzes check results to determine appropriate CSS classes,
285
- * icons, and text for visual status representation in the HTML report.
286
- *
287
- * @function getStatusDisplay
288
- * @param {HealthCheckResult} result - Check result object
289
- * @returns {StatusDisplay} Status display configuration
290
- *
291
- * @example
292
- * const display = getStatusDisplay({ success: true });
293
- * console.log(display.statusText); // "Pass"
294
- */
295
- function getStatusDisplay(result) {
296
- if (result.status === 'warning' || result.success === 'partial') {
297
- // Warning status
298
- return {
299
- statusClass: 'text-warning',
300
- statusIcon: '<i class="bi bi-exclamation-triangle-fill"></i>',
301
- statusText: 'Warning'
302
- };
303
- } else if (result.success) {
304
- // Pass status
305
- return {
306
- statusClass: 'text-success',
307
- statusIcon: '<i class="bi bi-check-circle-fill"></i>',
308
- statusText: 'Pass'
309
- };
310
- } else {
311
- // Fail status
312
- return {
313
- statusClass: 'text-danger',
314
- statusIcon: '<i class="bi bi-x-circle-fill"></i>',
315
- statusText: 'Fail'
316
- };
317
- }
318
- }
319
-
320
- /**
321
- * Main function to run all health checks and generate the report
322
- *
323
- * Orchestrates the execution of all available health check modules,
324
- * collects results, and generates a comprehensive HTML report.
325
- * Handles errors gracefully and ensures proper cleanup.
326
- *
327
- * @async
328
- * @function runHealthCheck
329
- * @throws {Error} When health check execution fails
330
- *
331
- * @description
332
- * The function performs the following checks:
333
- * - Term reference checks in external specs
334
- * - External specs URL validation
335
- * - Term references validation
336
- * - specs.json configuration validation
337
- * - Terms introduction file validation
338
- * - .gitignore destination directory check
339
- *
340
- * @example
341
- * try {
342
- * await runHealthCheck();
343
- * console.log('Health check completed successfully');
344
- * } catch (error) {
345
- * console.error('Health check failed:', error);
346
- * }
347
- */
348
- async function runHealthCheck() {
349
- Logger.info('Running health checks...');
350
-
351
- // Collection to store all check results
352
- const results = [];
353
-
354
- try {
355
-
356
- // Run term reference tref check
357
- const trefTermResults = await trefTermChecker.checkTrefTerms(process.cwd());
358
- results.push({
359
- title: 'Check Trefs in all external specs',
360
- results: trefTermResults
361
- });
362
-
363
- // Run external specs URL check
364
- const externalSpecsResults = await externalSpecsChecker.checkExternalSpecs(process.cwd());
365
- results.push({
366
- title: 'Check External Specs URL',
367
- results: externalSpecsResults
368
- });
369
-
370
- // Run term references check
371
- const termReferencesResults = await termReferencesChecker.checkTermReferences(process.cwd());
372
- results.push({
373
- title: 'Check Term References',
374
- results: termReferencesResults
375
- });
376
-
377
- // Run specs.json configuration check
378
- const specsConfigResults = await specsConfigurationChecker.checkSpecsJsonConfiguration(process.cwd());
379
- results.push({
380
- title: 'Check <code>specs.json</code> configuration',
381
- results: specsConfigResults
382
- });
383
-
384
- // Run terms-and-definitions-intro.md check
385
- const termsIntroResults = await termsIntroChecker.checkTermsIntroFile(process.cwd());
386
- results.push({
387
- title: 'Check <code>terms-and-definitions-intro.md</code>',
388
- results: termsIntroResults
389
- });
390
-
391
- // Run destination directory gitignore check
392
- const destinationGitignoreResults = await destinationGitignoreChecker.checkDestinationGitIgnore(process.cwd());
393
- results.push({
394
- title: 'Check <code>.gitignore</code>',
395
- results: destinationGitignoreResults
396
- });
397
-
398
- // Add more checks here in the future
399
-
400
- // Generate and open the report
401
- await generateReport(results);
402
- } catch (error) {
403
- console.error('Error running health checks:', error);
404
- process.exit(1);
405
- }
406
- }
407
-
408
- /**
409
- * Generates and opens an HTML health check report
410
- *
411
- * Creates a comprehensive HTML report with all health check results,
412
- * saves it to the cache directory, and opens it in the default browser.
413
- * The report includes interactive features like filtering and status indicators.
414
- *
415
- * @async
416
- * @function generateReport
417
- * @param {HealthCheckSection[]} checkResults - Array of health check result objects
418
- * @throws {Error} When report generation or file operations fail
419
- *
420
- * @example
421
- * const results = [
422
- * {
423
- * title: 'Configuration Check',
424
- * results: [{ name: 'specs.json', success: true, details: 'Valid' }]
425
- * }
426
- * ];
427
- * await generateReport(results);
428
- */
429
- async function generateReport(checkResults) {
430
- const timestamp = getFormattedTimestamp();
431
- // Get repository information from specs.json
432
- const repoInfo = await getRepoInfo();
433
- const reportFileName = `health-check-${timestamp}-${repoInfo.account}-${repoInfo.repo}.html`;
434
- const reportPath = path.join(OUTPUT_DIR, reportFileName);
435
-
436
44
 
437
- const htmlContent = generateHtmlReport(checkResults, getHumanReadableTimestamp(), repoInfo);
438
-
439
- fs.writeFileSync(reportPath, htmlContent);
440
- console.log(`Report generated: ${reportPath}`);
441
-
442
- // Open the report in the default browser
443
- try {
444
- const success = fileOpener.openHtmlFile(reportPath);
445
- if (!success) {
446
- console.error('Failed to open the report in browser');
447
- }
448
- } catch (error) {
449
- console.error('Failed to open the report:', error);
450
- }
45
+ // Handle process exit
46
+ child.on('exit', (code) => {
47
+ process.exit(code || 0);
48
+ });
49
+
50
+ // Handle errors
51
+ child.on('error', (error) => {
52
+ console.error('❌ Failed to run health check:', error.message);
53
+ console.error('\nTroubleshooting:');
54
+ console.error(' - Ensure spec-up-t-healthcheck is installed');
55
+ console.error(' - Try running: npm install in the spec-up-t package');
56
+ console.error(' - Check that Node.js is properly installed');
57
+ process.exit(1);
58
+ });
451
59
  }
452
60
 
453
- /**
454
- * Generates HTML content for the health check report
455
- *
456
- * Creates a complete HTML document with Bootstrap styling, interactive features,
457
- * and comprehensive health check results. Includes repository verification,
458
- * status filtering, and detailed result tables.
459
- *
460
- * @function generateHtmlReport
461
- * @param {HealthCheckSection[]} checkResults - Array of health check result sections
462
- * @param {string} timestamp - Human-readable timestamp for the report
463
- * @param {RepositoryInfo} repoInfo - Repository information object
464
- * @returns {string} Complete HTML document as string
465
- *
466
- * @example
467
- * const html = generateHtmlReport(results, '6/6/2025, 2:30:25 PM', {
468
- * host: 'github',
469
- * account: 'blockchain-bird',
470
- * repo: 'spec-up-t'
471
- * });
472
- */
473
- function generateHtmlReport(checkResults, timestamp, repoInfo) {
474
- let resultsHtml = '';
475
-
476
- // Add repository verification check at the beginning if needed
477
- if (repoInfo && repoInfo.verified === false) {
478
- const failStatus = {
479
- statusClass: 'text-danger',
480
- statusIcon: '<i class="bi bi-x-circle-fill"></i>',
481
- statusText: 'Fail'
482
- };
483
-
484
- // Create a new section at the top for repository verification
485
- resultsHtml += `
486
- <div class="card mb-4 results-card alert-danger" data-section="repository-verification">
487
- <div class="card-header bg-danger text-white">
488
- <h5>Repository Verification</h5>
489
- </div>
490
- <div class="card-body">
491
- <table class="table table-striped">
492
- <thead>
493
- <tr>
494
- <th>Status</th>
495
- <th>Check</th>
496
- <th>Details</th>
497
- </tr>
498
- </thead>
499
- <tbody>
500
- <tr data-status="fail" class="check-row">
501
- <td class="${failStatus.statusClass}" style="white-space: nowrap;">
502
- ${failStatus.statusIcon} <span style="vertical-align: middle;">${failStatus.statusText}</span>
503
- </td>
504
- <td>Repository existence check</td>
505
- <td>The repository at https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo} does not exist or is not accessible. Please verify the repository information in specs.json.</td>
506
- </tr>
507
- </tbody>
508
- </table>
509
- </div>
510
- </div>
511
- `;
512
- }
513
-
514
- checkResults.forEach(section => {
515
- resultsHtml += `
516
- <div class="card mb-4 results-card" data-section="${section.title.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}">
517
- <div class="card-header">
518
- <h5>${section.title}</h5>
519
- </div>
520
- <div class="card-body">
521
- <table class="table table-striped">
522
- <thead>
523
- <tr>
524
- <th>Status</th>
525
- <th>Check</th>
526
- <th>Details</th>
527
- </tr>
528
- </thead>
529
- <tbody>
530
- `;
531
-
532
- section.results.forEach(result => {
533
- const { statusClass, statusIcon, statusText } = getStatusDisplay(result);
534
-
535
- // Add data-status attribute to identify rows by status and reorder columns to put status first
536
- resultsHtml += `
537
- <tr data-status="${statusText.toLowerCase()}" class="check-row">
538
- <td class="${statusClass}" style="white-space: nowrap;">
539
- ${statusIcon} <span style="vertical-align: middle;">${statusText}</span>
540
- </td>
541
- <td>${result.name}</td>
542
- <td>${result.details || ''}</td>
543
- </tr>
544
- `;
545
- });
546
-
547
- resultsHtml += `
548
- </tbody>
549
- </table>
550
- </div>
551
- </div>
552
- `;
61
+ // Run the health check if this file is executed directly or required
62
+ if (require.main === module || process.env.SPEC_UP_T_HEALTH_CHECK_RUN !== 'false') {
63
+ runHealthCheck().catch(error => {
64
+ console.error('❌ Health check failed:', error);
65
+ process.exit(1);
553
66
  });
554
-
555
- return `
556
- <!DOCTYPE html>
557
- <html lang="en">
558
- <head>
559
- <meta charset="UTF-8">
560
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
561
- <title>Spec-Up-T Health Check Report</title>
562
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
563
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
564
- <style>
565
- body {
566
- padding-top: 2rem;
567
- padding-bottom: 2rem;
568
- }
569
- .report-header {
570
- margin-bottom: 2rem;
571
- }
572
- .timestamp {
573
- color: #6c757d;
574
- }
575
- .filter-toggle {
576
- margin-bottom: 1rem;
577
- }
578
- .hidden-item {
579
- display: none;
580
- }
581
- </style>
582
- </head>
583
- <body>
584
- <div class="container">
585
- <div class="report-header">
586
- <h1>Spec-Up-T Health Check Report</h1>
587
- <p class="timestamp">Generated: ${timestamp}</p>
588
- <p class="repo-info">
589
- Repository: <a href="https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}" target="_blank">https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}</a><br>
590
- Username: ${repoInfo.account}<br>
591
- Repo name: ${repoInfo.repo}
592
- </p>
593
- </div>
594
-
595
- <div class="filter-toggle form-check form-switch">
596
- <input class="form-check-input" type="checkbox" id="togglePassingChecks" checked>
597
- <label class="form-check-label" for="togglePassingChecks">Show / hide passing checks</label>
598
- </div>
599
-
600
- <div class="results">
601
- ${resultsHtml}
602
- </div>
603
- </div>
604
-
605
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
606
- <script>
607
- // Toggle function for passing checks
608
- document.getElementById('togglePassingChecks').addEventListener('change', function() {
609
- const showPassing = this.checked;
610
- const passingRows = document.querySelectorAll('tr[data-status="pass"]');
611
-
612
- // Hide/show passing rows
613
- passingRows.forEach(row => {
614
- if (showPassing) {
615
- row.classList.remove('hidden-item');
616
- } else {
617
- row.classList.add('hidden-item');
618
- }
619
- });
620
-
621
- // Check each results card to see if it should be hidden
622
- document.querySelectorAll('.results-card').forEach(card => {
623
- const visibleRows = card.querySelectorAll('tr.check-row:not(.hidden-item)');
624
-
625
- if (visibleRows.length === 0) {
626
- // If no visible rows, hide the entire card
627
- card.classList.add('hidden-item');
628
- } else {
629
- // Otherwise show it
630
- card.classList.remove('hidden-item');
631
- }
632
- });
633
- });
634
- </script>
635
- </body>
636
- </html>
637
- `;
638
67
  }
639
68
 
640
- /**
641
- * Script execution entry point
642
- *
643
- * Immediately executes the health check when this script is run directly.
644
- * This allows the script to be used as a standalone command-line tool.
645
- *
646
- * @example
647
- * // Run from command line:
648
- * // node src/health-check.js
649
- * // npm run healthCheck
650
- */
651
- runHealthCheck();
69
+ module.exports = runHealthCheck;