From 508204f6260d2c14bfb31c22ef0de8804eace11f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:35:13 +0000 Subject: [PATCH 01/15] Initial plan From 16afd40b693044f7163b4dd7947eabc6aec04dcb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:46:43 +0000 Subject: [PATCH 02/15] Implement validation service repository file listing Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .../DAKArtifactValidationService.ts | 155 +++++++++++++++--- src/services/validation/ValidationContext.ts | 48 ++++++ 2 files changed, 182 insertions(+), 21 deletions(-) diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index 916b513ca..34de42d17 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -226,21 +226,56 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - // TODO: Integrate with githubService to list all files - // For now, return empty report as placeholder - const fileResults: FileValidationResult[] = []; - - // Calculate summary - const summary = this.calculateSummary(fileResults); - - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary, - fileResults, - canSave: summary.filesWithErrors === 0, - duration: Date.now() - startTime - }; + try { + // Get all files from repository using githubService + const files = await this.context.getRepositoryFiles(owner, repo, branch); + + // Filter files based on component if specified + const filesToValidate = options?.component + ? files.filter(file => this.isComponentFile(file.path, options.component)) + : files; + + // Validate all files + const fileResults = await this.validateFiles( + filesToValidate.map(file => ({ + path: file.path, + content: file.content, + fileType: this.getFileType(file.path), + component: this.getComponentFromPath(file.path) + })) + ); + + // Calculate summary + const summary = this.calculateSummary(fileResults); + + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary, + fileResults, + canSave: summary.filesWithErrors === 0, + duration: Date.now() - startTime + }; + } catch (error) { + // If repository access fails, return report with error + console.error('Repository validation failed:', error); + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary: { + totalFiles: 0, + validFiles: 0, + filesWithErrors: 0, + filesWithWarnings: 0, + totalErrors: 0, + totalWarnings: 0, + totalInfo: 0 + }, + fileResults: [], + canSave: false, + duration: Date.now() - startTime + }; + } } /** @@ -263,12 +298,28 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - // Get rules for this component - const componentRules = this.registry.getRulesByComponent(component); - - // TODO: Integrate with githubService to list component files - // For now, return empty array as placeholder - return []; + try { + // Get all files from repository + const files = await this.context.getRepositoryFiles(owner, repo, branch); + + // Filter to only component files + const componentFiles = files.filter(file => this.isComponentFile(file.path, component)); + + // Validate component files + const fileResults = await this.validateFiles( + componentFiles.map(file => ({ + path: file.path, + content: file.content, + fileType: this.getFileType(file.path), + component: component + })) + ); + + return fileResults; + } catch (error) { + console.error('Component validation failed:', error); + return []; + } } /** @@ -375,6 +426,68 @@ export class DAKArtifactValidationService { return report.canSave; } + /** + * Get file type from file path + * + * @param filePath - File path + * @returns File type/extension + */ + private getFileType(filePath: string): string { + const extension = filePath.split('.').pop()?.toLowerCase(); + return extension || 'unknown'; + } + + /** + * Get DAK component from file path + * + * @param filePath - File path + * @returns DAK component type + */ + private getComponentFromPath(filePath: string): string { + const path = filePath.toLowerCase(); + + // Map directory patterns to DAK components + if (path.includes('/bpmn/') || path.endsWith('.bpmn')) { + return 'business-processes'; + } else if (path.includes('/dmn/') || path.endsWith('.dmn')) { + return 'decision-logic'; + } else if (path.includes('/fsh/') || path.endsWith('.fsh')) { + return 'fhir-profiles'; + } else if (path.includes('/questionnaires/') || path.includes('/forms/')) { + return 'data-entry-forms'; + } else if (path.includes('/valuesets/') || path.includes('/codesystems/')) { + return 'terminology'; + } else if (path.includes('sushi-config.yaml') || path.includes('dak.json')) { + return 'dak-config'; + } + + return 'unknown'; + } + + /** + * Check if file belongs to specified component + * + * @param filePath - File path + * @param component - DAK component type + * @returns true if file belongs to component + */ + private isComponentFile(filePath: string, component: string): boolean { + const fileComponent = this.getComponentFromPath(filePath); + + // Map component names to patterns + const componentMap: Record = { + 'business-processes': ['business-processes'], + 'decision-logic': ['decision-logic'], + 'fhir-profiles': ['fhir-profiles'], + 'dak-config': ['dak-config'], + 'data-entry-forms': ['data-entry-forms'], + 'terminology': ['terminology'] + }; + + const validComponents = componentMap[component] || [component]; + return validComponents.includes(fileComponent); + } + /** * Calculate summary statistics from file results * diff --git a/src/services/validation/ValidationContext.ts b/src/services/validation/ValidationContext.ts index 0101702c0..187bc93e8 100644 --- a/src/services/validation/ValidationContext.ts +++ b/src/services/validation/ValidationContext.ts @@ -292,6 +292,54 @@ export class ValidationContext implements IValidationContext { getRepositoryContext(): { owner: string; repo: string; branch: string } | undefined { return this.repositoryContext; } + + /** + * Get all files from repository recursively + * + * @param owner - Repository owner + * @param repo - Repository name + * @param branch - Branch name + * @param path - Starting path (defaults to root) + * @returns Array of file objects with path and content + */ + async getRepositoryFiles( + owner: string, + repo: string, + branch: string, + path: string = '' + ): Promise> { + const files: Array<{ path: string; content: string; }> = []; + + // Dynamic import of githubService to avoid circular dependencies + const { default: githubService } = await import('../githubService'); + + try { + const contents = await githubService.getDirectoryContents(owner, repo, path, branch); + + for (const item of contents) { + if (item.type === 'file') { + // Get file content + try { + const content = await githubService.getFileContent(owner, repo, item.path, branch); + files.push({ + path: item.path, + content: typeof content === 'string' ? content : JSON.stringify(content) + }); + } catch (error) { + console.warn(`Failed to fetch file ${item.path}:`, error); + } + } else if (item.type === 'dir') { + // Recursively fetch directory contents + const subFiles = await this.getRepositoryFiles(owner, repo, branch, item.path); + files.push(...subFiles); + } + } + } catch (error) { + console.error(`Failed to fetch repository contents at ${path}:`, error); + } + + return files; + } } // Export singleton instance From 0cb701436984db3e39f8a580f8df800c5dfb575b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:48:25 +0000 Subject: [PATCH 03/15] Register validation rules in validation service Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/services/validation/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/validation/index.ts b/src/services/validation/index.ts index 3e0f8d4ef..e929fe3b9 100644 --- a/src/services/validation/index.ts +++ b/src/services/validation/index.ts @@ -28,6 +28,7 @@ export * from './integration'; import { ValidationRuleRegistry } from './ValidationRuleRegistry'; import { validationContext } from './ValidationContext'; import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; +import { registerAllRules } from './rules'; // Create singleton registry export const validationRegistry = new ValidationRuleRegistry({ @@ -36,6 +37,9 @@ export const validationRegistry = new ValidationRuleRegistry({ throwOnDuplicate: false }); +// Register all validation rules +registerAllRules(validationRegistry); + // Create singleton validation service export const dakArtifactValidationService = createDAKArtifactValidationService( validationRegistry, From 44bbe2df647227f9cfa64d2c76be0180ce3210a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:50:40 +0000 Subject: [PATCH 04/15] Fix linter issues in validation services Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .../validation/DAKArtifactValidationService.ts | 1 - src/services/validation/index.ts | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index 34de42d17..d8e8be2a3 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -11,7 +11,6 @@ */ import { - ValidationRule, FileValidationResult, DAKValidationReport, ValidationViolation, diff --git a/src/services/validation/index.ts b/src/services/validation/index.ts index e929fe3b9..9ea6704f1 100644 --- a/src/services/validation/index.ts +++ b/src/services/validation/index.ts @@ -6,6 +6,11 @@ * @module validation */ +import { ValidationRuleRegistry } from './ValidationRuleRegistry'; +import { validationContext } from './ValidationContext'; +import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; +import { registerAllRules } from './rules'; + // Export types export * from './types'; @@ -24,12 +29,6 @@ export * from './rules'; // Export integration functions export * from './integration'; -// Create and export default validation service instance -import { ValidationRuleRegistry } from './ValidationRuleRegistry'; -import { validationContext } from './ValidationContext'; -import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; -import { registerAllRules } from './rules'; - // Create singleton registry export const validationRegistry = new ValidationRuleRegistry({ enableCache: true, From 4992ce5f6f45ecfb023646628f473033e5533bbb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Oct 2025 18:57:29 +0000 Subject: [PATCH 05/15] Fix accessibility issues in ValidationReport component Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .../validation/ValidationReport.tsx | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/components/validation/ValidationReport.tsx b/src/components/validation/ValidationReport.tsx index aa3a0058d..570f80b0c 100644 --- a/src/components/validation/ValidationReport.tsx +++ b/src/components/validation/ValidationReport.tsx @@ -121,10 +121,18 @@ export const ValidationReport: React.FC = ({ }, { errorCount: 0, warningCount: 0, infoCount: 0 }); return ( -
-
e.stopPropagation()}> +
+
-

Validation Report

+

Validation Report

diff --git a/src/components/validation/useValidation.ts b/src/components/validation/useValidation.ts index b63af9e7a..dbec7cab6 100644 --- a/src/components/validation/useValidation.ts +++ b/src/components/validation/useValidation.ts @@ -9,7 +9,7 @@ import { FileValidationResult, ComponentValidationOptions } from '../../services/validation/types'; -import { dakArtifactValidationService } from '../../services/validation'; +import { dakArtifactValidationService, ensureRulesRegistered } from '../../services/validation'; /** * Hook options for validation @@ -66,6 +66,9 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation setLoading(true); try { + // Ensure validation rules are registered before validation (lazy loading) + await ensureRulesRegistered(); + const validationReport = await dakArtifactValidationService.validateRepository( owner, repo, @@ -140,6 +143,9 @@ export function useFileValidation(): UseFileValidationReturn { setLoading(true); try { + // Ensure validation rules are registered before validation (lazy loading) + await ensureRulesRegistered(); + const fileResult = await dakArtifactValidationService.validateFile( filePath, content, @@ -213,6 +219,9 @@ export function useRepositoryValidation( setLoading(true); try { + // Ensure validation rules are registered before validation (lazy loading) + await ensureRulesRegistered(); + const validationReport = await dakArtifactValidationService.validateRepository( owner, repo, @@ -282,6 +291,9 @@ export function useComponentValidation(): UseComponentValidationReturn { setLoading(true); try { + // Ensure validation rules are registered before validation (lazy loading) + await ensureRulesRegistered(); + const validationReport = await dakArtifactValidationService.validateComponent( owner, repo, diff --git a/src/services/validation/index.ts b/src/services/validation/index.ts index 9ea6704f1..07081d8dc 100644 --- a/src/services/validation/index.ts +++ b/src/services/validation/index.ts @@ -9,7 +9,6 @@ import { ValidationRuleRegistry } from './ValidationRuleRegistry'; import { validationContext } from './ValidationContext'; import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; -import { registerAllRules } from './rules'; // Export types export * from './types'; @@ -36,11 +35,19 @@ export const validationRegistry = new ValidationRuleRegistry({ throwOnDuplicate: false }); -// Register all validation rules -registerAllRules(validationRegistry); - // Create singleton validation service export const dakArtifactValidationService = createDAKArtifactValidationService( validationRegistry, validationContext ); + +// Lazy load validation rules - only register when first validation is triggered +let rulesRegistered = false; +export async function ensureRulesRegistered(): Promise { + if (rulesRegistered) return; + + // Dynamic import to defer loading of all validation rules + const { registerAllRules } = await import('./rules'); + registerAllRules(validationRegistry); + rulesRegistered = true; +} From 3807b34f695aff3ef23255c6699aef9fb4c536d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:34:44 +0000 Subject: [PATCH 09/15] Fix module initialization by lazy loading validation service in hooks Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/components/validation/useValidation.ts | 33 ++++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/components/validation/useValidation.ts b/src/components/validation/useValidation.ts index dbec7cab6..37868b129 100644 --- a/src/components/validation/useValidation.ts +++ b/src/components/validation/useValidation.ts @@ -9,7 +9,18 @@ import { FileValidationResult, ComponentValidationOptions } from '../../services/validation/types'; -import { dakArtifactValidationService, ensureRulesRegistered } from '../../services/validation'; + +// Lazy load validation service to prevent module initialization issues +let validationServicePromise: Promise | null = null; +async function getValidationService() { + if (!validationServicePromise) { + validationServicePromise = import('../../services/validation').then(module => ({ + service: module.dakArtifactValidationService, + ensureRulesRegistered: module.ensureRulesRegistered + })); + } + return validationServicePromise; +} /** * Hook options for validation @@ -66,10 +77,11 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation setLoading(true); try { - // Ensure validation rules are registered before validation (lazy loading) + // Lazy load validation service and ensure rules are registered + const { service, ensureRulesRegistered } = await getValidationService(); await ensureRulesRegistered(); - const validationReport = await dakArtifactValidationService.validateRepository( + const validationReport = await service.validateRepository( owner, repo, branch @@ -143,10 +155,11 @@ export function useFileValidation(): UseFileValidationReturn { setLoading(true); try { - // Ensure validation rules are registered before validation (lazy loading) + // Lazy load validation service and ensure rules are registered + const { service, ensureRulesRegistered } = await getValidationService(); await ensureRulesRegistered(); - const fileResult = await dakArtifactValidationService.validateFile( + const fileResult = await service.validateFile( filePath, content, fileType, @@ -219,10 +232,11 @@ export function useRepositoryValidation( setLoading(true); try { - // Ensure validation rules are registered before validation (lazy loading) + // Lazy load validation service and ensure rules are registered + const { service, ensureRulesRegistered } = await getValidationService(); await ensureRulesRegistered(); - const validationReport = await dakArtifactValidationService.validateRepository( + const validationReport = await service.validateRepository( owner, repo, branch, @@ -291,10 +305,11 @@ export function useComponentValidation(): UseComponentValidationReturn { setLoading(true); try { - // Ensure validation rules are registered before validation (lazy loading) + // Lazy load validation service and ensure rules are registered + const { service, ensureRulesRegistered } = await getValidationService(); await ensureRulesRegistered(); - const validationReport = await dakArtifactValidationService.validateComponent( + const validationReport = await service.validateComponent( owner, repo, branch, From 180fb0776c0e63a7c9109448ac4f671da2f3556d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:38:33 +0000 Subject: [PATCH 10/15] Add comprehensive lazy loading policy to copilot instructions Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .github/copilot-instructions.md | 127 ++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 58b977e95..0733d3af5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -65,6 +65,133 @@ If you find yourself writing code that: --- +## 🚀 CRITICAL: LAZY LOADING POLICY 🚀 + +**MANDATORY FOR ALL COPILOT AGENTS**: Lazy loading is the **HIGHLY PREFERRED** method for loading libraries, services, components, and modules in SGeX Workbench. This is critical for performance, user experience, and preventing module initialization order issues. + +### Why Lazy Loading is Critical + +1. **Performance**: Reduces initial bundle size and improves time-to-interactive +2. **User Experience**: Faster page loads, especially on slow connections +3. **Module Initialization**: Prevents circular dependencies and initialization order issues +4. **Bandwidth**: Minimizes data transfer for features users may not use +5. **Scalability**: Allows the application to grow without impacting initial load time + +### Lazy Loading Requirements + +**YOU MUST** use lazy loading for: +- ✅ **React Components**: Use `React.lazy()` for all route-level and feature components +- ✅ **Services**: Use dynamic `import()` for heavy services or those with complex dependency chains +- ✅ **Libraries**: Defer loading of large libraries (BPMN.js, DMN.js, etc.) until needed +- ✅ **Validation Rules**: Register validation rules on-demand, not at module initialization +- ✅ **Heavy Dependencies**: Load XML/JSON parsers, formatters, etc. only when used + +**YOU MUST NOT**: +- ❌ Import services at module top-level if they trigger complex initialization chains +- ❌ Load large libraries eagerly when they're only used in specific features +- ❌ Initialize singletons at module load time if they have dependencies +- ❌ Import heavy components without React.lazy() or Suspense + +### Lazy Loading Patterns + +#### 1. React Components (Preferred) +```javascript +// ✅ CORRECT - Lazy load with React.lazy() +const ValidationReport = React.lazy(() => import('./validation/ValidationReport')); +const BPMNEditor = React.lazy(() => import('./BPMNEditor')); + +// Use with Suspense +Loading...
}> + + + +// ❌ WRONG - Eager import +import { ValidationReport } from './validation/ValidationReport'; +``` + +#### 2. Services and Libraries (Preferred) +```javascript +// ✅ CORRECT - Dynamic import with memoization +let servicePromise = null; +async function getValidationService() { + if (!servicePromise) { + servicePromise = import('../../services/validation').then(module => module.validationService); + } + return servicePromise; +} + +// Use in functions/hooks +const service = await getValidationService(); + +// ❌ WRONG - Top-level import +import { validationService } from '../../services/validation'; +``` + +#### 3. Heavy Libraries (Preferred) +```javascript +// ✅ CORRECT - Load BPMN.js only when needed +async function loadBPMNModeler() { + const BpmnJS = await import('bpmn-js/lib/Modeler'); + return new BpmnJS.default({ container: '#canvas' }); +} + +// ❌ WRONG - Load at module init +import BpmnModeler from 'bpmn-js/lib/Modeler'; +``` + +#### 4. Validation Rules (Preferred) +```javascript +// ✅ CORRECT - Register rules on first validation +let rulesRegistered = false; +export async function ensureRulesRegistered() { + if (rulesRegistered) return; + const { registerAllRules } = await import('./rules'); + registerAllRules(registry); + rulesRegistered = true; +} + +// ❌ WRONG - Register at module init +import { registerAllRules } from './rules'; +registerAllRules(registry); // Runs immediately +``` + +### Exception Cases + +Lazy loading may be skipped ONLY when: +1. **Critical path**: Component is needed for initial render (e.g., App.js, Router) +2. **Tiny modules**: Module is < 1KB and has no dependencies +3. **Already bundled**: Module is guaranteed to be in the same webpack chunk +4. **Explicit approval**: Code owner/maintainer explicitly approves eager loading + +**Document exceptions** with clear comments explaining why lazy loading is skipped. + +### Testing Lazy Loading + +When implementing lazy loading: +1. **Build and inspect**: Check webpack bundle analysis for separate chunks +2. **Network tab**: Verify chunks load on-demand in browser DevTools +3. **Performance**: Measure time-to-interactive before and after +4. **Error handling**: Ensure proper error boundaries for lazy-loaded components + +### Common Pitfalls to Avoid + +1. **Circular dependencies**: Lazy loading often reveals circular deps - fix the architecture +2. **Forgotten Suspense**: React.lazy() requires Suspense wrapper +3. **Promise caching**: Cache dynamic import promises to avoid loading twice +4. **Module initialization side effects**: Services with side effects at module level will still run - refactor to lazy init + +### Code Review Checklist + +Before submitting code, verify: +- [ ] All route-level components use React.lazy() +- [ ] Heavy services use dynamic import with memoization +- [ ] No services imported at top-level if they have complex initialization +- [ ] Suspense boundaries in place for lazy components +- [ ] Build output shows separate chunks for lazy-loaded modules +- [ ] No module initialization order issues in console + +--- + Following these guidelines will help us code together better: * If a branch is mentioned in a request, issue or bug report, then please update the context to refer to the branch mentioned. If no branch is mentioned, then assume it is main. * If there is no issue mentioned in a prompt or already in context, then propose to create an issue with an appropriate summary and title. From 31afac83b4613a96365e92d4d6956a1fb4e30e69 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 18:37:42 +0000 Subject: [PATCH 11/15] Isolate validation module by lazy loading entire validation section Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/components/DAKValidationSection.js | 78 ++++++++++++++++++++++++++ src/components/Publications.js | 76 ++++--------------------- 2 files changed, 88 insertions(+), 66 deletions(-) create mode 100644 src/components/DAKValidationSection.js diff --git a/src/components/DAKValidationSection.js b/src/components/DAKValidationSection.js new file mode 100644 index 000000000..13ec388e6 --- /dev/null +++ b/src/components/DAKValidationSection.js @@ -0,0 +1,78 @@ +/** + * DAK Validation Section Component + * + * Lazy-loaded component that encapsulates all validation functionality. + * This prevents the validation services from being loaded until this component is rendered. + */ + +import React, { useState } from 'react'; +import { useValidation } from './validation/useValidation'; +import { ValidationButton } from './validation/ValidationButton'; +import { ValidationReport } from './validation/ValidationReport'; +import { ValidationSummary } from './validation/ValidationSummary'; + +const DAKValidationSection = ({ owner, repo, branch }) => { + const [showValidationModal, setShowValidationModal] = useState(false); + const [validationComponent, setValidationComponent] = useState('all'); + + // Validation hook - only loaded when this component renders + const { report, loading: validating, validate } = useValidation({ + owner, + repo, + branch + }); + + return ( +
+
+

DAK Validation

+

+ Validate DAK artifacts against WHO SMART Guidelines standards. Check BPMN processes, + DMN decision tables, FSH profiles, and DAK-level compliance. +

+
+ +
+
+ + +
+ + validate({ component: validationComponent === 'all' ? undefined : validationComponent })} + loading={validating} + status={report ? (report.isValid ? 'success' : (report.summary.errorCount > 0 ? 'error' : 'warning')) : undefined} + label={validating ? 'Validating...' : 'Run Validation'} + /> +
+ + {report && ( +
+ setShowValidationModal(true)} + /> +
+ )} + + setShowValidationModal(false)} + /> +
+ ); +}; + +export default DAKValidationSection; diff --git a/src/components/Publications.js b/src/components/Publications.js index a7c21b834..15cd5cb12 100644 --- a/src/components/Publications.js +++ b/src/components/Publications.js @@ -2,12 +2,9 @@ import React, { useState, useEffect, Suspense } from 'react'; import githubService from '../services/githubService'; import StagingGround from './StagingGround'; import DAKPublicationGenerator from './DAKPublicationGenerator'; -import { useValidation } from './validation/useValidation'; -// Lazy load validation components for better performance -const ValidationButton = React.lazy(() => import('./validation/ValidationButton')); -const ValidationReport = React.lazy(() => import('./validation/ValidationReport')); -const ValidationSummary = React.lazy(() => import('./validation/ValidationSummary')); +// Lazy load entire validation section to prevent module initialization issues +const DAKValidationSection = React.lazy(() => import('./DAKValidationSection')); const Publications = ({ profile, repository, selectedBranch, hasWriteAccess }) => { const [branches, setBranches] = useState([]); @@ -15,19 +12,10 @@ const Publications = ({ profile, repository, selectedBranch, hasWriteAccess }) = const [workflowRuns, setWorkflowRuns] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const [showValidationModal, setShowValidationModal] = useState(false); - const [validationComponent, setValidationComponent] = useState('all'); const owner = repository.owner?.login || repository.full_name.split('/')[0]; const repoName = repository.name; - // Validation hook - const { report, loading: validating, validate } = useValidation({ - owner, - repo: repoName, - branch: selectedBranch || repository.default_branch - }); - useEffect(() => { const fetchPublicationData = async () => { try { @@ -246,58 +234,14 @@ const Publications = ({ profile, repository, selectedBranch, hasWriteAccess }) = profile={profile} /> - {/* DAK Validation Section */} -
-
-

DAK Validation

-

- Validate DAK artifacts against WHO SMART Guidelines standards. Check BPMN processes, - DMN decision tables, FSH profiles, and DAK-level compliance. -

-
- - Loading validation...
}> -
-
- - -
- - validate({ component: validationComponent === 'all' ? undefined : validationComponent })} - loading={validating} - status={report ? (report.isValid ? 'success' : (report.summary.errorCount > 0 ? 'error' : 'warning')) : undefined} - label={validating ? 'Validating...' : 'Run Validation'} - /> -
- - {report && ( -
- setShowValidationModal(true)} - /> -
- )} - - setShowValidationModal(false)} - /> - -
+ {/* DAK Validation Section - Lazy loaded to prevent module initialization issues */} + Loading validation...
}> + +

Published DAK Content

From fa952e6d3e24447c0f6243fdbd8f39b3b1f908fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 18:55:07 +0000 Subject: [PATCH 12/15] Revert ValidationContext changes to fix githubService module corruption Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .../DAKArtifactValidationService.ts | 97 +++++-------------- src/services/validation/ValidationContext.ts | 50 ---------- 2 files changed, 25 insertions(+), 122 deletions(-) diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index d8e8be2a3..14698fc99 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -225,56 +225,26 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - try { - // Get all files from repository using githubService - const files = await this.context.getRepositoryFiles(owner, repo, branch); - - // Filter files based on component if specified - const filesToValidate = options?.component - ? files.filter(file => this.isComponentFile(file.path, options.component)) - : files; - - // Validate all files - const fileResults = await this.validateFiles( - filesToValidate.map(file => ({ - path: file.path, - content: file.content, - fileType: this.getFileType(file.path), - component: this.getComponentFromPath(file.path) - })) - ); - - // Calculate summary - const summary = this.calculateSummary(fileResults); - - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary, - fileResults, - canSave: summary.filesWithErrors === 0, - duration: Date.now() - startTime - }; - } catch (error) { - // If repository access fails, return report with error - console.error('Repository validation failed:', error); - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary: { - totalFiles: 0, - validFiles: 0, - filesWithErrors: 0, - filesWithWarnings: 0, - totalErrors: 0, - totalWarnings: 0, - totalInfo: 0 - }, - fileResults: [], - canSave: false, - duration: Date.now() - startTime - }; - } + // TODO: Implement repository file fetching without breaking githubService + // For now, return empty report to prevent module initialization issues + console.warn('validateRepository not yet implemented - returning empty report'); + + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary: { + totalFiles: 0, + validFiles: 0, + filesWithErrors: 0, + filesWithWarnings: 0, + totalErrors: 0, + totalWarnings: 0, + totalInfo: 0 + }, + fileResults: [], + canSave: true, + duration: Date.now() - startTime + }; } /** @@ -297,28 +267,11 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - try { - // Get all files from repository - const files = await this.context.getRepositoryFiles(owner, repo, branch); - - // Filter to only component files - const componentFiles = files.filter(file => this.isComponentFile(file.path, component)); - - // Validate component files - const fileResults = await this.validateFiles( - componentFiles.map(file => ({ - path: file.path, - content: file.content, - fileType: this.getFileType(file.path), - component: component - })) - ); - - return fileResults; - } catch (error) { - console.error('Component validation failed:', error); - return []; - } + // TODO: Implement component validation without breaking githubService + // For now, return empty array to prevent module initialization issues + console.warn('validateComponent not yet implemented - returning empty array'); + + return []; } /** diff --git a/src/services/validation/ValidationContext.ts b/src/services/validation/ValidationContext.ts index b66e4e0b2..0101702c0 100644 --- a/src/services/validation/ValidationContext.ts +++ b/src/services/validation/ValidationContext.ts @@ -292,56 +292,6 @@ export class ValidationContext implements IValidationContext { getRepositoryContext(): { owner: string; repo: string; branch: string } | undefined { return this.repositoryContext; } - - /** - * Get all files from repository recursively - * - * @param owner - Repository owner - * @param repo - Repository name - * @param branch - Branch name - * @param path - Starting path (defaults to root) - * @returns Array of file objects with path and content - */ - async getRepositoryFiles( - owner: string, - repo: string, - branch: string, - path: string = '' - ): Promise> { - const files: Array<{ path: string; content: string; }> = []; - - // Lazy load githubService to avoid initialization order issues - // This prevents the validation module from affecting githubService exports - const githubServiceModule = await import('../githubService'); - const githubService = githubServiceModule.default; - - try { - const contents = await githubService.getDirectoryContents(owner, repo, path, branch); - - for (const item of contents) { - if (item.type === 'file') { - // Get file content - try { - const content = await githubService.getFileContent(owner, repo, item.path, branch); - files.push({ - path: item.path, - content: typeof content === 'string' ? content : JSON.stringify(content) - }); - } catch (error) { - console.warn(`Failed to fetch file ${item.path}:`, error); - } - } else if (item.type === 'dir') { - // Recursively fetch directory contents - const subFiles = await this.getRepositoryFiles(owner, repo, branch, item.path); - files.push(...subFiles); - } - } - } catch (error) { - console.error(`Failed to fetch repository contents at ${path}:`, error); - } - - return files; - } } // Export singleton instance From a9d49f300ce6908554c611f38ea60cf7a439ba8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:14:12 +0000 Subject: [PATCH 13/15] Implement full validation service with direct githubService import Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- .../DAKArtifactValidationService.ts | 97 ++++++++++++++----- src/services/validation/ValidationContext.ts | 47 +++++++++ 2 files changed, 119 insertions(+), 25 deletions(-) diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index 14698fc99..0b8b950bc 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -225,26 +225,56 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - // TODO: Implement repository file fetching without breaking githubService - // For now, return empty report to prevent module initialization issues - console.warn('validateRepository not yet implemented - returning empty report'); - - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary: { - totalFiles: 0, - validFiles: 0, - filesWithErrors: 0, - filesWithWarnings: 0, - totalErrors: 0, - totalWarnings: 0, - totalInfo: 0 - }, - fileResults: [], - canSave: true, - duration: Date.now() - startTime - }; + try { + // Get all files from repository + const files = await this.context.getRepositoryFiles(owner, repo, branch); + + // Filter files based on component if specified + const filesToValidate = options?.component + ? files.filter(file => this.isComponentFile(file.path, options.component)) + : files; + + // Validate all files + const fileResults = await this.validateFiles( + filesToValidate.map(file => ({ + path: file.path, + content: file.content, + fileType: this.getFileType(file.path), + component: this.getComponentFromPath(file.path) + })) + ); + + // Calculate summary + const summary = this.calculateSummary(fileResults); + + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary, + fileResults, + canSave: summary.filesWithErrors === 0, + duration: Date.now() - startTime + }; + } catch (error) { + // If repository access fails, return report with error + console.error('Repository validation failed:', error); + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary: { + totalFiles: 0, + validFiles: 0, + filesWithErrors: 0, + filesWithWarnings: 0, + totalErrors: 0, + totalWarnings: 0, + totalInfo: 0 + }, + fileResults: [], + canSave: false, + duration: Date.now() - startTime + }; + } } /** @@ -267,11 +297,28 @@ export class DAKArtifactValidationService { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - // TODO: Implement component validation without breaking githubService - // For now, return empty array to prevent module initialization issues - console.warn('validateComponent not yet implemented - returning empty array'); - - return []; + try { + // Get all files from repository + const files = await this.context.getRepositoryFiles(owner, repo, branch); + + // Filter to only component files + const componentFiles = files.filter(file => this.isComponentFile(file.path, component)); + + // Validate component files + const fileResults = await this.validateFiles( + componentFiles.map(file => ({ + path: file.path, + content: file.content, + fileType: this.getFileType(file.path), + component: component + })) + ); + + return fileResults; + } catch (error) { + console.error('Component validation failed:', error); + return []; + } } /** diff --git a/src/services/validation/ValidationContext.ts b/src/services/validation/ValidationContext.ts index 0101702c0..a20fb1801 100644 --- a/src/services/validation/ValidationContext.ts +++ b/src/services/validation/ValidationContext.ts @@ -8,6 +8,7 @@ */ import { ValidationContext as IValidationContext } from './types'; +import githubService from '../githubService'; /** * Validation Context Implementation @@ -292,6 +293,52 @@ export class ValidationContext implements IValidationContext { getRepositoryContext(): { owner: string; repo: string; branch: string } | undefined { return this.repositoryContext; } + + /** + * Get all files from repository recursively + * Uses direct import of githubService to avoid webpack bundling issues + * + * @param owner - Repository owner + * @param repo - Repository name + * @param branch - Branch name + * @param path - Starting path (defaults to root) + * @returns Array of file objects with path and content + */ + async getRepositoryFiles( + owner: string, + repo: string, + branch: string, + path: string = '' + ): Promise> { + const files: Array<{ path: string; content: string; }> = []; + + try { + const contents = await githubService.getDirectoryContents(owner, repo, path, branch); + + for (const item of contents) { + if (item.type === 'file') { + // Get file content + try { + const content = await githubService.getFileContent(owner, repo, item.path, branch); + files.push({ + path: item.path, + content: typeof content === 'string' ? content : JSON.stringify(content) + }); + } catch (error) { + console.warn(`Failed to fetch file ${item.path}:`, error); + } + } else if (item.type === 'dir') { + // Recursively fetch directory contents + const subFiles = await this.getRepositoryFiles(owner, repo, branch, item.path); + files.push(...subFiles); + } + } + } catch (error) { + console.error(`Failed to fetch repository contents at ${path}:`, error); + } + + return files; + } } // Export singleton instance From 61933a7b93ef1c234fa2b2d0467e2487a773f352 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 00:07:16 +0000 Subject: [PATCH 14/15] Fix module corruption by moving githubService import to useValidation hook Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/components/validation/useValidation.ts | 54 +++++++++++++++++-- .../DAKArtifactValidationService.ts | 41 ++++++++++---- src/services/validation/ValidationContext.ts | 47 ---------------- 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/src/components/validation/useValidation.ts b/src/components/validation/useValidation.ts index 37868b129..133f60ee0 100644 --- a/src/components/validation/useValidation.ts +++ b/src/components/validation/useValidation.ts @@ -22,6 +22,45 @@ async function getValidationService() { return validationServicePromise; } +/** + * Fetch all files from repository recursively + * Helper function to fetch files using githubService without importing it at module level + */ +async function fetchRepositoryFiles( + githubService: any, + owner: string, + repo: string, + branch: string, + path: string = '' +): Promise> { + const files: Array<{ path: string; content: string; }> = []; + + try { + const contents = await githubService.getDirectoryContents(owner, repo, path, branch); + + for (const item of contents) { + if (item.type === 'file') { + try { + const content = await githubService.getFileContent(owner, repo, item.path, branch); + files.push({ + path: item.path, + content: typeof content === 'string' ? content : JSON.stringify(content) + }); + } catch (error) { + console.warn(`Failed to fetch file ${item.path}:`, error); + } + } else if (item.type === 'dir') { + const subFiles = await fetchRepositoryFiles(githubService, owner, repo, branch, item.path); + files.push(...subFiles); + } + } + } catch (error) { + console.error(`Failed to fetch repository contents at ${path}:`, error); + } + + return files; +} + /** * Hook options for validation */ @@ -49,7 +88,7 @@ export interface UseValidationReturn { /** Error state */ error: Error | null; /** Trigger validation */ - validate: () => Promise; + validate: (options?: ComponentValidationOptions) => Promise; /** Clear current report */ clear: () => void; } @@ -66,7 +105,7 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation const debounceTimer = useRef(undefined); - const validate = useCallback(async () => { + const validate = useCallback(async (validateOptions?: ComponentValidationOptions) => { if (!owner || !repo) { setError(new Error('Repository owner and name required')); return; @@ -81,10 +120,19 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation const { service, ensureRulesRegistered } = await getValidationService(); await ensureRulesRegistered(); + // Dynamically import githubService to fetch files + const githubServiceModule = await import('../../services/githubService'); + const githubService = githubServiceModule.default; + + // Fetch all files from repository + const files = await fetchRepositoryFiles(githubService, owner, repo, branch); + + // Pass files to validation service const validationReport = await service.validateRepository( owner, repo, - branch + branch, + { ...validateOptions, files } ); setReport(validationReport); } catch (err) { diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index 0b8b950bc..b61409e7e 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -218,7 +218,7 @@ export class DAKArtifactValidationService { owner: string, repo: string, branch: string, - options?: ComponentValidationOptions + options?: ComponentValidationOptions & { files?: Array<{ path: string; content: string; }> } ): Promise { const startTime = Date.now(); @@ -226,13 +226,31 @@ export class DAKArtifactValidationService { this.context.setRepositoryContext({ owner, repo, branch }); try { - // Get all files from repository - const files = await this.context.getRepositoryFiles(owner, repo, branch); + // Files should be provided by caller to avoid importing githubService here + if (!options?.files || options.files.length === 0) { + console.warn('No files provided for validation. Caller must fetch files using githubService.'); + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary: { + totalFiles: 0, + validFiles: 0, + filesWithErrors: 0, + filesWithWarnings: 0, + totalErrors: 0, + totalWarnings: 0, + totalInfo: 0 + }, + fileResults: [], + canSave: true, + duration: Date.now() - startTime + }; + } // Filter files based on component if specified const filesToValidate = options?.component - ? files.filter(file => this.isComponentFile(file.path, options.component)) - : files; + ? options.files.filter(file => this.isComponentFile(file.path, options.component)) + : options.files; // Validate all files const fileResults = await this.validateFiles( @@ -256,7 +274,7 @@ export class DAKArtifactValidationService { duration: Date.now() - startTime }; } catch (error) { - // If repository access fails, return report with error + // If validation fails, return report with error console.error('Repository validation failed:', error); return { repository: { owner, repo, branch }, @@ -292,17 +310,20 @@ export class DAKArtifactValidationService { repo: string, branch: string, component: string, - options?: Omit + options?: Omit & { files?: Array<{ path: string; content: string; }> } ): Promise { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); try { - // Get all files from repository - const files = await this.context.getRepositoryFiles(owner, repo, branch); + // Files should be provided by caller to avoid importing githubService here + if (!options?.files || options.files.length === 0) { + console.warn('No files provided for validation. Caller must fetch files using githubService.'); + return []; + } // Filter to only component files - const componentFiles = files.filter(file => this.isComponentFile(file.path, component)); + const componentFiles = options.files.filter(file => this.isComponentFile(file.path, component)); // Validate component files const fileResults = await this.validateFiles( diff --git a/src/services/validation/ValidationContext.ts b/src/services/validation/ValidationContext.ts index a20fb1801..0101702c0 100644 --- a/src/services/validation/ValidationContext.ts +++ b/src/services/validation/ValidationContext.ts @@ -8,7 +8,6 @@ */ import { ValidationContext as IValidationContext } from './types'; -import githubService from '../githubService'; /** * Validation Context Implementation @@ -293,52 +292,6 @@ export class ValidationContext implements IValidationContext { getRepositoryContext(): { owner: string; repo: string; branch: string } | undefined { return this.repositoryContext; } - - /** - * Get all files from repository recursively - * Uses direct import of githubService to avoid webpack bundling issues - * - * @param owner - Repository owner - * @param repo - Repository name - * @param branch - Branch name - * @param path - Starting path (defaults to root) - * @returns Array of file objects with path and content - */ - async getRepositoryFiles( - owner: string, - repo: string, - branch: string, - path: string = '' - ): Promise> { - const files: Array<{ path: string; content: string; }> = []; - - try { - const contents = await githubService.getDirectoryContents(owner, repo, path, branch); - - for (const item of contents) { - if (item.type === 'file') { - // Get file content - try { - const content = await githubService.getFileContent(owner, repo, item.path, branch); - files.push({ - path: item.path, - content: typeof content === 'string' ? content : JSON.stringify(content) - }); - } catch (error) { - console.warn(`Failed to fetch file ${item.path}:`, error); - } - } else if (item.type === 'dir') { - // Recursively fetch directory contents - const subFiles = await this.getRepositoryFiles(owner, repo, branch, item.path); - files.push(...subFiles); - } - } - } catch (error) { - console.error(`Failed to fetch repository contents at ${path}:`, error); - } - - return files; - } } // Export singleton instance From 77aeb4ce84126e1af255a0aba1f21fdd06915c4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 01:04:21 +0000 Subject: [PATCH 15/15] Revert validation service implementations to original baseline to fix webpack corruption Co-authored-by: litlfred <662242+litlfred@users.noreply.github.com> --- src/components/validation/useValidation.ts | 91 +-------- .../DAKArtifactValidationService.ts | 181 +++--------------- src/services/validation/index.ts | 20 +- 3 files changed, 37 insertions(+), 255 deletions(-) diff --git a/src/components/validation/useValidation.ts b/src/components/validation/useValidation.ts index 133f60ee0..b63af9e7a 100644 --- a/src/components/validation/useValidation.ts +++ b/src/components/validation/useValidation.ts @@ -9,57 +9,7 @@ import { FileValidationResult, ComponentValidationOptions } from '../../services/validation/types'; - -// Lazy load validation service to prevent module initialization issues -let validationServicePromise: Promise | null = null; -async function getValidationService() { - if (!validationServicePromise) { - validationServicePromise = import('../../services/validation').then(module => ({ - service: module.dakArtifactValidationService, - ensureRulesRegistered: module.ensureRulesRegistered - })); - } - return validationServicePromise; -} - -/** - * Fetch all files from repository recursively - * Helper function to fetch files using githubService without importing it at module level - */ -async function fetchRepositoryFiles( - githubService: any, - owner: string, - repo: string, - branch: string, - path: string = '' -): Promise> { - const files: Array<{ path: string; content: string; }> = []; - - try { - const contents = await githubService.getDirectoryContents(owner, repo, path, branch); - - for (const item of contents) { - if (item.type === 'file') { - try { - const content = await githubService.getFileContent(owner, repo, item.path, branch); - files.push({ - path: item.path, - content: typeof content === 'string' ? content : JSON.stringify(content) - }); - } catch (error) { - console.warn(`Failed to fetch file ${item.path}:`, error); - } - } else if (item.type === 'dir') { - const subFiles = await fetchRepositoryFiles(githubService, owner, repo, branch, item.path); - files.push(...subFiles); - } - } - } catch (error) { - console.error(`Failed to fetch repository contents at ${path}:`, error); - } - - return files; -} +import { dakArtifactValidationService } from '../../services/validation'; /** * Hook options for validation @@ -88,7 +38,7 @@ export interface UseValidationReturn { /** Error state */ error: Error | null; /** Trigger validation */ - validate: (options?: ComponentValidationOptions) => Promise; + validate: () => Promise; /** Clear current report */ clear: () => void; } @@ -105,7 +55,7 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation const debounceTimer = useRef(undefined); - const validate = useCallback(async (validateOptions?: ComponentValidationOptions) => { + const validate = useCallback(async () => { if (!owner || !repo) { setError(new Error('Repository owner and name required')); return; @@ -116,23 +66,10 @@ export function useValidation(options: UseValidationOptions = {}): UseValidation setLoading(true); try { - // Lazy load validation service and ensure rules are registered - const { service, ensureRulesRegistered } = await getValidationService(); - await ensureRulesRegistered(); - - // Dynamically import githubService to fetch files - const githubServiceModule = await import('../../services/githubService'); - const githubService = githubServiceModule.default; - - // Fetch all files from repository - const files = await fetchRepositoryFiles(githubService, owner, repo, branch); - - // Pass files to validation service - const validationReport = await service.validateRepository( + const validationReport = await dakArtifactValidationService.validateRepository( owner, repo, - branch, - { ...validateOptions, files } + branch ); setReport(validationReport); } catch (err) { @@ -203,11 +140,7 @@ export function useFileValidation(): UseFileValidationReturn { setLoading(true); try { - // Lazy load validation service and ensure rules are registered - const { service, ensureRulesRegistered } = await getValidationService(); - await ensureRulesRegistered(); - - const fileResult = await service.validateFile( + const fileResult = await dakArtifactValidationService.validateFile( filePath, content, fileType, @@ -280,11 +213,7 @@ export function useRepositoryValidation( setLoading(true); try { - // Lazy load validation service and ensure rules are registered - const { service, ensureRulesRegistered } = await getValidationService(); - await ensureRulesRegistered(); - - const validationReport = await service.validateRepository( + const validationReport = await dakArtifactValidationService.validateRepository( owner, repo, branch, @@ -353,11 +282,7 @@ export function useComponentValidation(): UseComponentValidationReturn { setLoading(true); try { - // Lazy load validation service and ensure rules are registered - const { service, ensureRulesRegistered } = await getValidationService(); - await ensureRulesRegistered(); - - const validationReport = await service.validateComponent( + const validationReport = await dakArtifactValidationService.validateComponent( owner, repo, branch, diff --git a/src/services/validation/DAKArtifactValidationService.ts b/src/services/validation/DAKArtifactValidationService.ts index b61409e7e..916b513ca 100644 --- a/src/services/validation/DAKArtifactValidationService.ts +++ b/src/services/validation/DAKArtifactValidationService.ts @@ -11,6 +11,7 @@ */ import { + ValidationRule, FileValidationResult, DAKValidationReport, ValidationViolation, @@ -218,81 +219,28 @@ export class DAKArtifactValidationService { owner: string, repo: string, branch: string, - options?: ComponentValidationOptions & { files?: Array<{ path: string; content: string; }> } + options?: ComponentValidationOptions ): Promise { const startTime = Date.now(); // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - try { - // Files should be provided by caller to avoid importing githubService here - if (!options?.files || options.files.length === 0) { - console.warn('No files provided for validation. Caller must fetch files using githubService.'); - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary: { - totalFiles: 0, - validFiles: 0, - filesWithErrors: 0, - filesWithWarnings: 0, - totalErrors: 0, - totalWarnings: 0, - totalInfo: 0 - }, - fileResults: [], - canSave: true, - duration: Date.now() - startTime - }; - } - - // Filter files based on component if specified - const filesToValidate = options?.component - ? options.files.filter(file => this.isComponentFile(file.path, options.component)) - : options.files; - - // Validate all files - const fileResults = await this.validateFiles( - filesToValidate.map(file => ({ - path: file.path, - content: file.content, - fileType: this.getFileType(file.path), - component: this.getComponentFromPath(file.path) - })) - ); - - // Calculate summary - const summary = this.calculateSummary(fileResults); - - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary, - fileResults, - canSave: summary.filesWithErrors === 0, - duration: Date.now() - startTime - }; - } catch (error) { - // If validation fails, return report with error - console.error('Repository validation failed:', error); - return { - repository: { owner, repo, branch }, - timestamp: new Date(), - summary: { - totalFiles: 0, - validFiles: 0, - filesWithErrors: 0, - filesWithWarnings: 0, - totalErrors: 0, - totalWarnings: 0, - totalInfo: 0 - }, - fileResults: [], - canSave: false, - duration: Date.now() - startTime - }; - } + // TODO: Integrate with githubService to list all files + // For now, return empty report as placeholder + const fileResults: FileValidationResult[] = []; + + // Calculate summary + const summary = this.calculateSummary(fileResults); + + return { + repository: { owner, repo, branch }, + timestamp: new Date(), + summary, + fileResults, + canSave: summary.filesWithErrors === 0, + duration: Date.now() - startTime + }; } /** @@ -310,36 +258,17 @@ export class DAKArtifactValidationService { repo: string, branch: string, component: string, - options?: Omit & { files?: Array<{ path: string; content: string; }> } + options?: Omit ): Promise { // Set repository context this.context.setRepositoryContext({ owner, repo, branch }); - try { - // Files should be provided by caller to avoid importing githubService here - if (!options?.files || options.files.length === 0) { - console.warn('No files provided for validation. Caller must fetch files using githubService.'); - return []; - } - - // Filter to only component files - const componentFiles = options.files.filter(file => this.isComponentFile(file.path, component)); - - // Validate component files - const fileResults = await this.validateFiles( - componentFiles.map(file => ({ - path: file.path, - content: file.content, - fileType: this.getFileType(file.path), - component: component - })) - ); - - return fileResults; - } catch (error) { - console.error('Component validation failed:', error); - return []; - } + // Get rules for this component + const componentRules = this.registry.getRulesByComponent(component); + + // TODO: Integrate with githubService to list component files + // For now, return empty array as placeholder + return []; } /** @@ -446,68 +375,6 @@ export class DAKArtifactValidationService { return report.canSave; } - /** - * Get file type from file path - * - * @param filePath - File path - * @returns File type/extension - */ - private getFileType(filePath: string): string { - const extension = filePath.split('.').pop()?.toLowerCase(); - return extension || 'unknown'; - } - - /** - * Get DAK component from file path - * - * @param filePath - File path - * @returns DAK component type - */ - private getComponentFromPath(filePath: string): string { - const path = filePath.toLowerCase(); - - // Map directory patterns to DAK components - if (path.includes('/bpmn/') || path.endsWith('.bpmn')) { - return 'business-processes'; - } else if (path.includes('/dmn/') || path.endsWith('.dmn')) { - return 'decision-logic'; - } else if (path.includes('/fsh/') || path.endsWith('.fsh')) { - return 'fhir-profiles'; - } else if (path.includes('/questionnaires/') || path.includes('/forms/')) { - return 'data-entry-forms'; - } else if (path.includes('/valuesets/') || path.includes('/codesystems/')) { - return 'terminology'; - } else if (path.includes('sushi-config.yaml') || path.includes('dak.json')) { - return 'dak-config'; - } - - return 'unknown'; - } - - /** - * Check if file belongs to specified component - * - * @param filePath - File path - * @param component - DAK component type - * @returns true if file belongs to component - */ - private isComponentFile(filePath: string, component: string): boolean { - const fileComponent = this.getComponentFromPath(filePath); - - // Map component names to patterns - const componentMap: Record = { - 'business-processes': ['business-processes'], - 'decision-logic': ['decision-logic'], - 'fhir-profiles': ['fhir-profiles'], - 'dak-config': ['dak-config'], - 'data-entry-forms': ['data-entry-forms'], - 'terminology': ['terminology'] - }; - - const validComponents = componentMap[component] || [component]; - return validComponents.includes(fileComponent); - } - /** * Calculate summary statistics from file results * diff --git a/src/services/validation/index.ts b/src/services/validation/index.ts index 07081d8dc..3e0f8d4ef 100644 --- a/src/services/validation/index.ts +++ b/src/services/validation/index.ts @@ -6,10 +6,6 @@ * @module validation */ -import { ValidationRuleRegistry } from './ValidationRuleRegistry'; -import { validationContext } from './ValidationContext'; -import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; - // Export types export * from './types'; @@ -28,6 +24,11 @@ export * from './rules'; // Export integration functions export * from './integration'; +// Create and export default validation service instance +import { ValidationRuleRegistry } from './ValidationRuleRegistry'; +import { validationContext } from './ValidationContext'; +import { createDAKArtifactValidationService } from './DAKArtifactValidationService'; + // Create singleton registry export const validationRegistry = new ValidationRuleRegistry({ enableCache: true, @@ -40,14 +41,3 @@ export const dakArtifactValidationService = createDAKArtifactValidationService( validationRegistry, validationContext ); - -// Lazy load validation rules - only register when first validation is triggered -let rulesRegistered = false; -export async function ensureRulesRegistered(): Promise { - if (rulesRegistered) return; - - // Dynamic import to defer loading of all validation rules - const { registerAllRules } = await import('./rules'); - registerAllRules(validationRegistry); - rulesRegistered = true; -}