@unrdf/project-engine 5.0.1 → 26.4.2

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 (39) hide show
  1. package/package.json +16 -15
  2. package/src/golden-structure.mjs +2 -2
  3. package/src/materialize-apply.mjs +2 -2
  4. package/README.md +0 -53
  5. package/src/api-contract-validator.mjs +0 -711
  6. package/src/auto-test-generator.mjs +0 -444
  7. package/src/autonomic-mapek.mjs +0 -511
  8. package/src/capabilities-manifest.mjs +0 -125
  9. package/src/code-complexity-js.mjs +0 -368
  10. package/src/dependency-graph.mjs +0 -276
  11. package/src/doc-drift-checker.mjs +0 -172
  12. package/src/doc-generator.mjs +0 -229
  13. package/src/domain-infer.mjs +0 -966
  14. package/src/drift-snapshot.mjs +0 -775
  15. package/src/file-roles.mjs +0 -94
  16. package/src/fs-scan.mjs +0 -305
  17. package/src/gap-finder.mjs +0 -376
  18. package/src/hotspot-analyzer.mjs +0 -412
  19. package/src/index.mjs +0 -151
  20. package/src/initialize.mjs +0 -957
  21. package/src/lens/project-structure.mjs +0 -74
  22. package/src/mapek-orchestration.mjs +0 -665
  23. package/src/materialize-plan.mjs +0 -422
  24. package/src/materialize.mjs +0 -137
  25. package/src/policy-derivation.mjs +0 -869
  26. package/src/project-config.mjs +0 -142
  27. package/src/project-diff.mjs +0 -28
  28. package/src/project-engine/build-utils.mjs +0 -237
  29. package/src/project-engine/code-analyzer.mjs +0 -248
  30. package/src/project-engine/doc-generator.mjs +0 -407
  31. package/src/project-engine/infrastructure.mjs +0 -213
  32. package/src/project-engine/metrics.mjs +0 -146
  33. package/src/project-model.mjs +0 -111
  34. package/src/project-report.mjs +0 -348
  35. package/src/refactoring-guide.mjs +0 -242
  36. package/src/stack-detect.mjs +0 -102
  37. package/src/stack-linter.mjs +0 -213
  38. package/src/template-infer.mjs +0 -674
  39. package/src/type-auditor.mjs +0 -609
@@ -1,172 +0,0 @@
1
- /**
2
- * @file Doc Drift Checker - detects documentation out of sync with code
3
- * @module project-engine/doc-drift-checker
4
- */
5
-
6
- import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
7
- import { z } from 'zod';
8
-
9
- const { namedNode } = DataFactory;
10
-
11
- const NS = {
12
- fs: 'http://example.org/unrdf/filesystem#',
13
- proj: 'http://example.org/unrdf/project#',
14
- };
15
-
16
- const DocDriftOptionsSchema = z.object({
17
- projectStore: z.custom(val => val && typeof val.getQuads === 'function', {
18
- message: 'projectStore must be an RDF store with getQuads method',
19
- }),
20
- projectRoot: z.string().optional(),
21
- });
22
-
23
- const DriftEntrySchema = z.object({
24
- docFile: z.string(),
25
- sourceFile: z.string().optional(),
26
- driftType: z.enum(['outdated', 'missing', 'orphaned', 'incomplete']),
27
- severity: z.enum(['critical', 'high', 'medium', 'low']),
28
- reason: z.string(),
29
- suggestion: z.string(),
30
- });
31
-
32
- /**
33
- * Check for documentation drift
34
- * @param {Object} options
35
- * @param {Store} options.projectStore - Project RDF store
36
- * @returns {{ drifts: Array, summary: string, healthScore: number }}
37
- */
38
- export function checkDocDrift(options) {
39
- const validated = DocDriftOptionsSchema.parse(options);
40
- const { projectStore } = validated;
41
-
42
- const drifts = [];
43
- const fileQuads = projectStore.getQuads(null, namedNode(NS.fs + 'relativePath'), null);
44
- const allFiles = fileQuads.map(q => ({ path: q.object.value }));
45
-
46
- const docFiles = allFiles.filter(f => isDocFile(f.path));
47
- const sourceFiles = allFiles.filter(f => isSourceFile(f.path));
48
-
49
- for (const source of sourceFiles) {
50
- if (needsDocumentation(source.path)) {
51
- const hasDoc = docFiles.some(d => docMatchesSource(d.path, source.path));
52
- if (!hasDoc) {
53
- drifts.push({
54
- sourceFile: source.path,
55
- docFile: suggestDocPath(source.path),
56
- driftType: 'missing',
57
- severity: determineSeverity(source.path),
58
- reason: 'Source file has no documentation',
59
- suggestion: 'Create documentation at ' + suggestDocPath(source.path),
60
- });
61
- }
62
- }
63
- }
64
-
65
- for (const doc of docFiles) {
66
- if (!isStandaloneDoc(doc.path)) {
67
- const matchedSource = sourceFiles.find(s => docMatchesSource(doc.path, s.path));
68
- if (!matchedSource) {
69
- drifts.push({
70
- docFile: doc.path,
71
- driftType: 'orphaned',
72
- severity: 'low',
73
- reason: 'Documentation has no matching source',
74
- suggestion: 'Review and update or remove',
75
- });
76
- }
77
- }
78
- }
79
-
80
- const readme = docFiles.find(d => d.path.toLowerCase().includes('readme'));
81
- if (!readme && sourceFiles.length > 10) {
82
- drifts.push({
83
- docFile: 'README.md',
84
- driftType: 'missing',
85
- severity: 'critical',
86
- reason: 'Project has no README.md',
87
- suggestion: 'Create README.md',
88
- });
89
- }
90
-
91
- const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
92
- drifts.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
93
-
94
- const totalNeeded = sourceFiles.filter(s => needsDocumentation(s.path)).length;
95
- const documented = totalNeeded - drifts.filter(d => d.driftType === 'missing').length;
96
- const healthScore = totalNeeded > 0 ? Math.round((documented / totalNeeded) * 100) : 100;
97
-
98
- const summary =
99
- drifts.length > 0
100
- ? drifts.length + ' documentation drift issues (' + healthScore + '% documented)'
101
- : 'Documentation is up to date (' + healthScore + '% coverage)';
102
-
103
- return { drifts, summary, healthScore };
104
- }
105
-
106
- function isDocFile(filePath) {
107
- return /\.(md|mdx|txt|rst)$/.test(filePath) || filePath.includes('/docs/');
108
- }
109
-
110
- function isSourceFile(filePath) {
111
- return /\.(tsx?|jsx?|mjs)$/.test(filePath) && !isTestFile(filePath) && !isConfigFile(filePath);
112
- }
113
-
114
- function isTestFile(filePath) {
115
- return /\.(test|spec)\.(tsx?|jsx?|mjs)$/.test(filePath);
116
- }
117
-
118
- function isConfigFile(filePath) {
119
- return /\.(config|rc)\.(tsx?|jsx?|mjs|json)$/.test(filePath);
120
- }
121
-
122
- function needsDocumentation(filePath) {
123
- return filePath.includes('/api/') || filePath.includes('index.') || filePath.includes('/lib/');
124
- }
125
-
126
- function docMatchesSource(docPath, sourcePath) {
127
- const docName = docPath
128
- .toLowerCase()
129
- .replace(/\.mdx?$/, '')
130
- .split('/')
131
- .pop();
132
- const sourceName = sourcePath
133
- .toLowerCase()
134
- .replace(/\.(tsx?|jsx?|mjs)$/, '')
135
- .split('/')
136
- .pop();
137
- return docName === sourceName || (docName && docName.includes(sourceName || ''));
138
- }
139
-
140
- function isStandaloneDoc(docPath) {
141
- const patterns = ['readme', 'contributing', 'changelog', 'license', 'guide', 'architecture'];
142
- const filename = docPath.toLowerCase().split('/').pop() || '';
143
- return patterns.some(p => filename.includes(p));
144
- }
145
-
146
- function suggestDocPath(sourcePath) {
147
- const filename = sourcePath.split('/').pop();
148
- const mdName = filename ? filename.replace(/\.(tsx?|jsx?|mjs)$/, '.md') : 'doc.md';
149
- return 'docs/' + mdName;
150
- }
151
-
152
- function determineSeverity(filePath) {
153
- if (filePath.includes('/api/')) return 'high';
154
- if (filePath.includes('index.')) return 'medium';
155
- return 'low';
156
- }
157
-
158
- export { DriftEntrySchema };
159
-
160
- // Alias exports for backwards compatibility with existing index.mjs
161
- export const checkDocConsistency = checkDocDrift;
162
- export const extractDocReferences = options => {
163
- const result = checkDocDrift(options);
164
- return result.drifts.map(d => ({
165
- docFile: d.docFile,
166
- sourceFile: d.sourceFile,
167
- }));
168
- };
169
- export const scoreDocDrift = options => {
170
- const result = checkDocDrift(options);
171
- return { score: 100 - result.healthScore, driftCount: result.drifts.length };
172
- };
@@ -1,229 +0,0 @@
1
- /**
2
- * @file Doc Generator - generates documentation suggestions from code analysis
3
- * @module project-engine/doc-generator
4
- */
5
-
6
- import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
7
- import { z } from 'zod';
8
-
9
- const { namedNode } = DataFactory;
10
-
11
- const NS = {
12
- rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
13
- fs: 'http://example.org/unrdf/filesystem#',
14
- dom: 'http://example.org/unrdf/domain#',
15
- };
16
-
17
- const DocGeneratorOptionsSchema = z.object({
18
- projectStore: z.custom(val => val && typeof val.getQuads === 'function', {
19
- message: 'projectStore must be an RDF store with getQuads method',
20
- }),
21
- domainStore: z.custom(val => val && typeof val.getQuads === 'function').optional(),
22
- stackProfile: z.object({}).passthrough().optional(),
23
- projectRoot: z.string().optional(),
24
- });
25
-
26
- const DocSuggestionSchema = z.object({
27
- docType: z.enum(['readme', 'api', 'architecture', 'guide', 'jsdoc', 'changelog']),
28
- targetPath: z.string(),
29
- priority: z.enum(['critical', 'high', 'medium', 'low']),
30
- title: z.string(),
31
- sections: z.array(z.string()),
32
- relatedFiles: z.array(z.string()),
33
- });
34
-
35
- /**
36
- * Generate documentation suggestions
37
- * @param {Object} options
38
- * @param {Store} options.projectStore - Project RDF store
39
- * @param {Store} [options.domainStore] - Domain model store
40
- * @param {Object} [options.stackProfile] - Stack profile
41
- * @returns {{ suggestions: Array, summary: string, coverage: number }}
42
- */
43
- export function generateDocSuggestions(options) {
44
- const validated = DocGeneratorOptionsSchema.parse(options);
45
- const { projectStore, domainStore, stackProfile } = validated;
46
-
47
- const suggestions = [];
48
- const fileQuads = projectStore.getQuads(null, namedNode(`${NS.fs}relativePath`), null);
49
- const allFiles = fileQuads.map(q => ({ path: q.object.value }));
50
-
51
- const sourceFiles = allFiles.filter(f => isSourceFile(f.path));
52
- const docFiles = allFiles.filter(f => isDocFile(f.path));
53
-
54
- // Check 1: README.md
55
- const hasReadme = docFiles.some(f => f.path.toLowerCase().includes('readme'));
56
- if (!hasReadme) {
57
- const entities = extractEntities(domainStore);
58
- suggestions.push({
59
- docType: 'readme',
60
- targetPath: 'README.md',
61
- priority: 'critical',
62
- title: 'Project README',
63
- sections: generateReadmeSections(sourceFiles, entities, stackProfile),
64
- relatedFiles: [],
65
- });
66
- }
67
-
68
- // Check 2: API documentation
69
- const apiFiles = sourceFiles.filter(f => isApiFile(f.path));
70
- const hasApiDocs = docFiles.some(f => f.path.includes('api') || f.path.includes('openapi'));
71
- if (apiFiles.length > 0 && !hasApiDocs) {
72
- suggestions.push({
73
- docType: 'api',
74
- targetPath: 'docs/api/README.md',
75
- priority: 'high',
76
- title: 'API Documentation',
77
- sections: generateApiDocSections(apiFiles),
78
- relatedFiles: apiFiles.map(f => f.path),
79
- });
80
- }
81
-
82
- // Check 3: Architecture documentation
83
- const hasArchDocs = docFiles.some(
84
- f => f.path.includes('architecture') || f.path.includes('design')
85
- );
86
- if (sourceFiles.length > 20 && !hasArchDocs) {
87
- suggestions.push({
88
- docType: 'architecture',
89
- targetPath: 'docs/ARCHITECTURE.md',
90
- priority: 'medium',
91
- title: 'Architecture Overview',
92
- sections: [
93
- '## System Overview',
94
- '## Directory Structure',
95
- '## Key Components',
96
- '## Data Flow',
97
- ],
98
- relatedFiles: getKeyFiles(sourceFiles),
99
- });
100
- }
101
-
102
- // Check 4: Getting started guide
103
- const hasGuide = docFiles.some(
104
- f => f.path.includes('guide') || f.path.includes('getting-started')
105
- );
106
- if (sourceFiles.length > 10 && !hasGuide) {
107
- suggestions.push({
108
- docType: 'guide',
109
- targetPath: 'docs/GETTING_STARTED.md',
110
- priority: 'medium',
111
- title: 'Getting Started Guide',
112
- sections: ['## Prerequisites', '## Installation', '## Quick Start', '## Development'],
113
- relatedFiles: [],
114
- });
115
- }
116
-
117
- // Check 5: Changelog
118
- const hasChangelog = docFiles.some(f => f.path.toLowerCase().includes('changelog'));
119
- if (!hasChangelog) {
120
- suggestions.push({
121
- docType: 'changelog',
122
- targetPath: 'CHANGELOG.md',
123
- priority: 'low',
124
- title: 'Changelog',
125
- sections: ['[Unreleased]', 'Added', 'Changed', 'Fixed', 'Removed'],
126
- relatedFiles: [],
127
- });
128
- }
129
-
130
- const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
131
- suggestions.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
132
-
133
- const totalNeeded = calculateTotalDocsNeeded(sourceFiles, apiFiles);
134
- const existing = docFiles.length;
135
- const coverage =
136
- totalNeeded > 0 ? Math.min(100, Math.round((existing / totalNeeded) * 100)) : 100;
137
-
138
- const summary =
139
- suggestions.length > 0
140
- ? `${suggestions.length} documentation suggestions (${coverage}% coverage)`
141
- : `Documentation is comprehensive (${coverage}% coverage)`;
142
-
143
- return { suggestions, summary, coverage };
144
- }
145
-
146
- function isSourceFile(filePath) {
147
- return (
148
- /\.(tsx?|jsx?|mjs)$/.test(filePath) &&
149
- !filePath.includes('.test.') &&
150
- !filePath.includes('.config.')
151
- );
152
- }
153
-
154
- function isDocFile(filePath) {
155
- return /\.(md|mdx|txt|rst)$/.test(filePath);
156
- }
157
-
158
- function isApiFile(filePath) {
159
- return (
160
- filePath.includes('/api/') || filePath.includes('route.') || filePath.includes('controller.')
161
- );
162
- }
163
-
164
- function extractEntities(domainStore) {
165
- if (!domainStore) return [];
166
- const entityQuads = domainStore.getQuads(
167
- null,
168
- namedNode(`${NS.rdf}type`),
169
- namedNode(`${NS.dom}Entity`)
170
- );
171
- return entityQuads
172
- .map(q => q.subject.value.split('#').pop() || q.subject.value.split('/').pop())
173
- .filter(Boolean);
174
- }
175
-
176
- function generateReadmeSections(sourceFiles, entities, stackProfile) {
177
- const sections = ['## Overview', '## Features', '## Installation', '## Quick Start'];
178
- if (entities.length > 0) sections.push('## Domain Entities');
179
- if (sourceFiles.some(f => f.path.includes('/api/'))) sections.push('## API Reference');
180
- sections.push('## Configuration', '## Development');
181
- if (stackProfile?.testFramework) sections.push('## Testing');
182
- sections.push('## License');
183
- return sections;
184
- }
185
-
186
- function generateApiDocSections(apiFiles) {
187
- const sections = ['## Overview'];
188
- const endpoints = apiFiles.map(f => {
189
- const path = f.path;
190
- if (path.includes('/app/api/')) {
191
- const match = path.match(/\/app(\/api\/[^/]+(?:\/[^/]+)*?)\/route/);
192
- return match ? match[1] : path;
193
- }
194
- return path.split('/').slice(-2, -1)[0] || 'endpoint';
195
- });
196
- const uniqueEndpoints = [...new Set(endpoints)];
197
- for (const endpoint of uniqueEndpoints.slice(0, 10)) sections.push(`### ${endpoint}`);
198
- sections.push('## Authentication', '## Error Handling');
199
- return sections;
200
- }
201
-
202
- function getKeyFiles(sourceFiles) {
203
- return sourceFiles
204
- .filter(f => f.path.includes('index.') || f.path.includes('main.') || f.path.includes('app.'))
205
- .map(f => f.path)
206
- .slice(0, 10);
207
- }
208
-
209
- function calculateTotalDocsNeeded(sourceFiles, apiFiles) {
210
- return 4 + Math.ceil(apiFiles.length / 5) + Math.ceil(sourceFiles.length / 20);
211
- }
212
-
213
- export { DocSuggestionSchema };
214
-
215
- // Alias exports for backwards compatibility with existing index.mjs
216
- export const generateEntityReference = (options, entity) => ({
217
- entity,
218
- markdown: '# ' + entity + ' Reference',
219
- });
220
- export const generateAPIReference = _options => ({
221
- markdown: '# API Reference',
222
- endpoints: [],
223
- });
224
- export const generateArchitectureDiagram = _options => ({
225
- mermaid: 'graph TD',
226
- components: [],
227
- });
228
- export const generateCompleteDocumentation = generateDocSuggestions;
229
- export const DocGenerationResultSchema = DocSuggestionSchema;