@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.
- package/package.json +16 -15
- package/src/golden-structure.mjs +2 -2
- package/src/materialize-apply.mjs +2 -2
- package/README.md +0 -53
- package/src/api-contract-validator.mjs +0 -711
- package/src/auto-test-generator.mjs +0 -444
- package/src/autonomic-mapek.mjs +0 -511
- package/src/capabilities-manifest.mjs +0 -125
- package/src/code-complexity-js.mjs +0 -368
- package/src/dependency-graph.mjs +0 -276
- package/src/doc-drift-checker.mjs +0 -172
- package/src/doc-generator.mjs +0 -229
- package/src/domain-infer.mjs +0 -966
- package/src/drift-snapshot.mjs +0 -775
- package/src/file-roles.mjs +0 -94
- package/src/fs-scan.mjs +0 -305
- package/src/gap-finder.mjs +0 -376
- package/src/hotspot-analyzer.mjs +0 -412
- package/src/index.mjs +0 -151
- package/src/initialize.mjs +0 -957
- package/src/lens/project-structure.mjs +0 -74
- package/src/mapek-orchestration.mjs +0 -665
- package/src/materialize-plan.mjs +0 -422
- package/src/materialize.mjs +0 -137
- package/src/policy-derivation.mjs +0 -869
- package/src/project-config.mjs +0 -142
- package/src/project-diff.mjs +0 -28
- package/src/project-engine/build-utils.mjs +0 -237
- package/src/project-engine/code-analyzer.mjs +0 -248
- package/src/project-engine/doc-generator.mjs +0 -407
- package/src/project-engine/infrastructure.mjs +0 -213
- package/src/project-engine/metrics.mjs +0 -146
- package/src/project-model.mjs +0 -111
- package/src/project-report.mjs +0 -348
- package/src/refactoring-guide.mjs +0 -242
- package/src/stack-detect.mjs +0 -102
- package/src/stack-linter.mjs +0 -213
- package/src/template-infer.mjs +0 -674
- package/src/type-auditor.mjs +0 -609
package/src/initialize.mjs
DELETED
|
@@ -1,957 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Project initialization pipeline - orchestrates all 8 capabilities
|
|
3
|
-
* @module project-engine/initialize
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import { createHash } from 'crypto';
|
|
8
|
-
import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
|
|
9
|
-
import { createStore } from '@unrdf/oxigraph'; // TODO: Replace with Oxigraph Store
|
|
10
|
-
import { scanFileSystemToStore } from './fs-scan.mjs';
|
|
11
|
-
import { detectStackFromFs } from './stack-detect.mjs';
|
|
12
|
-
import { buildProjectModelFromFs } from './project-model.mjs';
|
|
13
|
-
import { classifyFiles } from './file-roles.mjs';
|
|
14
|
-
import { analyzeJsComplexity } from './code-complexity-js.mjs';
|
|
15
|
-
|
|
16
|
-
const { namedNode, literal } = DataFactory;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} PhaseReceipt
|
|
20
|
-
* @property {number} duration - Phase duration in ms
|
|
21
|
-
* @property {boolean} success - Phase success status
|
|
22
|
-
* @property {string} [error] - Error message if failed
|
|
23
|
-
* @property {Object} [data] - Phase-specific data
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @typedef {Object} InitializationReceipt
|
|
28
|
-
* @property {Object} phases - Receipt for each phase
|
|
29
|
-
* @property {number} totalDuration - Total duration in ms
|
|
30
|
-
* @property {boolean} success - Overall success
|
|
31
|
-
* @property {Object} [metrics] - Code complexity metrics summary (from Phase 6.5)
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Object} InitializationState
|
|
36
|
-
* @property {Store} fsStore - Filesystem RDF store
|
|
37
|
-
* @property {Store} projectStore - Project model store
|
|
38
|
-
* @property {Store} domainStore - Domain inference store
|
|
39
|
-
* @property {Store} [complexityStore] - Code complexity analysis store
|
|
40
|
-
* @property {Object} templateGraph - Template inference graph
|
|
41
|
-
* @property {Object} snapshot - Baseline snapshot
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @typedef {Object} InitializationResult
|
|
46
|
-
* @property {boolean} success
|
|
47
|
-
* @property {InitializationReceipt} receipt
|
|
48
|
-
* @property {Object} report
|
|
49
|
-
* @property {InitializationState} state
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
const InitializeOptionsSchema = z.object({
|
|
53
|
-
ignorePatterns: z.array(z.string()).optional(),
|
|
54
|
-
baseIri: z.string().default('http://example.org/unrdf/'),
|
|
55
|
-
conventions: z
|
|
56
|
-
.object({
|
|
57
|
-
sourcePaths: z.array(z.string()).default(['src']),
|
|
58
|
-
featurePaths: z.array(z.string()).default(['features', 'modules']),
|
|
59
|
-
testPaths: z.array(z.string()).default(['__tests__', 'test', 'tests', 'spec']),
|
|
60
|
-
})
|
|
61
|
-
.optional(),
|
|
62
|
-
skipPhases: z.array(z.string()).optional(),
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Create and execute the project initialization pipeline
|
|
67
|
-
*
|
|
68
|
-
* @param {string} projectRoot - Root directory of the project
|
|
69
|
-
* @param {Object} [options] - Pipeline options
|
|
70
|
-
* @param {string[]} [options.ignorePatterns] - Patterns to ignore during scan
|
|
71
|
-
* @param {string} [options.baseIri] - Base IRI for RDF resources
|
|
72
|
-
* @param {Object} [options.conventions] - Naming conventions
|
|
73
|
-
* @param {string[]} [options.skipPhases] - Phases to skip
|
|
74
|
-
* @returns {Promise<InitializationResult>}
|
|
75
|
-
*/
|
|
76
|
-
export async function createProjectInitializationPipeline(projectRoot, options = {}) {
|
|
77
|
-
const validated = InitializeOptionsSchema.parse(options);
|
|
78
|
-
const startTime = Date.now();
|
|
79
|
-
|
|
80
|
-
const phases = {};
|
|
81
|
-
const state = {
|
|
82
|
-
fsStore: null,
|
|
83
|
-
projectStore: null,
|
|
84
|
-
domainStore: null,
|
|
85
|
-
templateGraph: null,
|
|
86
|
-
snapshot: null,
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const skipPhases = new Set(validated.skipPhases || []);
|
|
90
|
-
|
|
91
|
-
// Phase 1: Scan FS
|
|
92
|
-
if (!skipPhases.has('scan')) {
|
|
93
|
-
phases.scan = await executeScanPhase(projectRoot, validated);
|
|
94
|
-
if (!phases.scan.success) {
|
|
95
|
-
return buildFailureResult(phases, startTime, 'scan', phases.scan.error);
|
|
96
|
-
}
|
|
97
|
-
state.fsStore = phases.scan.data.store;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Phase 2: Detect stack
|
|
101
|
-
if (!skipPhases.has('stackDetection') && state.fsStore) {
|
|
102
|
-
phases.stackDetection = executeStackDetectionPhase(state.fsStore, validated);
|
|
103
|
-
if (!phases.stackDetection.success) {
|
|
104
|
-
return buildFailureResult(phases, startTime, 'stackDetection', phases.stackDetection.error);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Phase 3: Build project model
|
|
109
|
-
if (!skipPhases.has('projectModel') && state.fsStore) {
|
|
110
|
-
phases.projectModel = executeProjectModelPhase(state.fsStore, validated);
|
|
111
|
-
if (!phases.projectModel.success) {
|
|
112
|
-
return buildFailureResult(phases, startTime, 'projectModel', phases.projectModel.error);
|
|
113
|
-
}
|
|
114
|
-
state.projectStore = phases.projectModel.data.store;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Phase 4: Classify file roles
|
|
118
|
-
if (!skipPhases.has('fileRoles') && state.projectStore) {
|
|
119
|
-
phases.fileRoles = executeFileRolesPhase(
|
|
120
|
-
state.projectStore,
|
|
121
|
-
phases.stackDetection?.data?.profile,
|
|
122
|
-
validated
|
|
123
|
-
);
|
|
124
|
-
if (!phases.fileRoles.success) {
|
|
125
|
-
return buildFailureResult(phases, startTime, 'fileRoles', phases.fileRoles.error);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Phase 5: Infer domain model
|
|
130
|
-
if (!skipPhases.has('domainInference') && state.projectStore) {
|
|
131
|
-
phases.domainInference = executeDomainInferencePhase(state.projectStore, validated);
|
|
132
|
-
if (!phases.domainInference.success) {
|
|
133
|
-
return buildFailureResult(phases, startTime, 'domainInference', phases.domainInference.error);
|
|
134
|
-
}
|
|
135
|
-
state.domainStore = phases.domainInference.data.store;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Phase 6.5: Analyze code complexity (JavaScript/TypeScript)
|
|
139
|
-
if (!skipPhases.has('codeComplexity')) {
|
|
140
|
-
phases.codeComplexity = await executeCodeComplexityPhase(
|
|
141
|
-
projectRoot,
|
|
142
|
-
state.domainStore,
|
|
143
|
-
validated
|
|
144
|
-
);
|
|
145
|
-
if (!phases.codeComplexity.success) {
|
|
146
|
-
return buildFailureResult(phases, startTime, 'codeComplexity', phases.codeComplexity.error);
|
|
147
|
-
}
|
|
148
|
-
state.complexityStore = phases.codeComplexity.data.store;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Phase 6: Infer templates
|
|
152
|
-
if (!skipPhases.has('templateInference') && state.domainStore) {
|
|
153
|
-
phases.templateInference = executeTemplateInferencePhase(state.domainStore, validated);
|
|
154
|
-
if (!phases.templateInference.success) {
|
|
155
|
-
return buildFailureResult(
|
|
156
|
-
phases,
|
|
157
|
-
startTime,
|
|
158
|
-
'templateInference',
|
|
159
|
-
phases.templateInference.error
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
state.templateGraph = phases.templateInference.data.templateGraph;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Phase 7: Create baseline snapshot
|
|
166
|
-
if (!skipPhases.has('snapshot')) {
|
|
167
|
-
phases.snapshot = executeSnapshotPhase(state, validated);
|
|
168
|
-
if (!phases.snapshot.success) {
|
|
169
|
-
return buildFailureResult(phases, startTime, 'snapshot', phases.snapshot.error);
|
|
170
|
-
}
|
|
171
|
-
state.snapshot = phases.snapshot.data.snapshot;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Phase 8: Derive and register hooks
|
|
175
|
-
if (!skipPhases.has('hooks') && state.domainStore) {
|
|
176
|
-
phases.hooks = executeHooksPhase(state, validated);
|
|
177
|
-
if (!phases.hooks.success) {
|
|
178
|
-
return buildFailureResult(phases, startTime, 'hooks', phases.hooks.error);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Phase 9: Generate report
|
|
183
|
-
phases.report = generateReportPhase(state, phases, validated);
|
|
184
|
-
|
|
185
|
-
const totalDuration = Date.now() - startTime;
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
success: true,
|
|
189
|
-
receipt: {
|
|
190
|
-
phases,
|
|
191
|
-
totalDuration,
|
|
192
|
-
success: true,
|
|
193
|
-
metrics: phases.codeComplexity?.data?.summary,
|
|
194
|
-
},
|
|
195
|
-
report: phases.report.data.report,
|
|
196
|
-
state,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Execute filesystem scan phase
|
|
202
|
-
*
|
|
203
|
-
* @private
|
|
204
|
-
* @param {string} projectRoot
|
|
205
|
-
* @param {Object} options
|
|
206
|
-
* @returns {Promise<PhaseReceipt>}
|
|
207
|
-
*/
|
|
208
|
-
async function executeScanPhase(projectRoot, options) {
|
|
209
|
-
const startTime = Date.now();
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
const { store, summary } = await scanFileSystemToStore({
|
|
213
|
-
root: projectRoot,
|
|
214
|
-
ignorePatterns: options.ignorePatterns,
|
|
215
|
-
baseIri: `${options.baseIri}fs#`,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
duration: Date.now() - startTime,
|
|
220
|
-
success: true,
|
|
221
|
-
data: {
|
|
222
|
-
store,
|
|
223
|
-
files: summary.fileCount,
|
|
224
|
-
folders: summary.folderCount,
|
|
225
|
-
ignored: summary.ignoredCount,
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
} catch (error) {
|
|
229
|
-
return {
|
|
230
|
-
duration: Date.now() - startTime,
|
|
231
|
-
success: false,
|
|
232
|
-
error: error.message,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Execute stack detection phase
|
|
239
|
-
*
|
|
240
|
-
* @private
|
|
241
|
-
* @param {Store} fsStore
|
|
242
|
-
* @param {Object} options
|
|
243
|
-
* @returns {PhaseReceipt}
|
|
244
|
-
*/
|
|
245
|
-
function executeStackDetectionPhase(fsStore, options) {
|
|
246
|
-
const startTime = Date.now();
|
|
247
|
-
|
|
248
|
-
try {
|
|
249
|
-
const profile = detectStackFromFs({
|
|
250
|
-
fsStore,
|
|
251
|
-
projectIri: `${options.baseIri}project#project`,
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
const frameworks = [];
|
|
255
|
-
if (profile.uiFramework) frameworks.push(profile.uiFramework);
|
|
256
|
-
if (profile.webFramework) frameworks.push(profile.webFramework);
|
|
257
|
-
if (profile.apiFramework && profile.apiFramework !== profile.webFramework) {
|
|
258
|
-
frameworks.push(profile.apiFramework);
|
|
259
|
-
}
|
|
260
|
-
if (profile.testFramework) frameworks.push(profile.testFramework);
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
duration: Date.now() - startTime,
|
|
264
|
-
success: true,
|
|
265
|
-
data: {
|
|
266
|
-
profile,
|
|
267
|
-
frameworks,
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
} catch (error) {
|
|
271
|
-
return {
|
|
272
|
-
duration: Date.now() - startTime,
|
|
273
|
-
success: false,
|
|
274
|
-
error: error.message,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Execute project model building phase
|
|
281
|
-
*
|
|
282
|
-
* @private
|
|
283
|
-
* @param {Store} fsStore
|
|
284
|
-
* @param {Object} options
|
|
285
|
-
* @returns {PhaseReceipt}
|
|
286
|
-
*/
|
|
287
|
-
function executeProjectModelPhase(fsStore, options) {
|
|
288
|
-
const startTime = Date.now();
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
const store = buildProjectModelFromFs({
|
|
292
|
-
fsStore,
|
|
293
|
-
baseIri: `${options.baseIri}project#`,
|
|
294
|
-
conventions: options.conventions,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Count features
|
|
298
|
-
const featureQuads = store.getQuads(
|
|
299
|
-
null,
|
|
300
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
301
|
-
namedNode('http://example.org/unrdf/project#Feature')
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
// Count files
|
|
305
|
-
const fileQuads = store.getQuads(
|
|
306
|
-
null,
|
|
307
|
-
namedNode('http://example.org/unrdf/filesystem#relativePath'),
|
|
308
|
-
null
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
duration: Date.now() - startTime,
|
|
313
|
-
success: true,
|
|
314
|
-
data: {
|
|
315
|
-
store,
|
|
316
|
-
features: featureQuads.length,
|
|
317
|
-
files: fileQuads.length,
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
} catch (error) {
|
|
321
|
-
return {
|
|
322
|
-
duration: Date.now() - startTime,
|
|
323
|
-
success: false,
|
|
324
|
-
error: error.message,
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Execute file roles classification phase
|
|
331
|
-
*
|
|
332
|
-
* @private
|
|
333
|
-
* @param {Store} projectStore
|
|
334
|
-
* @param {Object} stackProfile
|
|
335
|
-
* @param {Object} options
|
|
336
|
-
* @returns {PhaseReceipt}
|
|
337
|
-
*/
|
|
338
|
-
function executeFileRolesPhase(projectStore, stackProfile, options) {
|
|
339
|
-
const startTime = Date.now();
|
|
340
|
-
|
|
341
|
-
try {
|
|
342
|
-
const store = classifyFiles({
|
|
343
|
-
fsStore: projectStore,
|
|
344
|
-
stackInfo: stackProfile,
|
|
345
|
-
baseIri: `${options.baseIri}project#`,
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// Count classified files
|
|
349
|
-
const classifiedQuads = store.getQuads(
|
|
350
|
-
null,
|
|
351
|
-
namedNode('http://example.org/unrdf/project#roleString'),
|
|
352
|
-
null
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
// Count total files
|
|
356
|
-
const totalQuads = store.getQuads(
|
|
357
|
-
null,
|
|
358
|
-
namedNode('http://example.org/unrdf/filesystem#relativePath'),
|
|
359
|
-
null
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
duration: Date.now() - startTime,
|
|
364
|
-
success: true,
|
|
365
|
-
data: {
|
|
366
|
-
classified: classifiedQuads.length,
|
|
367
|
-
unclassified: totalQuads.length - classifiedQuads.length,
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
} catch (error) {
|
|
371
|
-
return {
|
|
372
|
-
duration: Date.now() - startTime,
|
|
373
|
-
success: false,
|
|
374
|
-
error: error.message,
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Execute domain inference phase
|
|
381
|
-
*
|
|
382
|
-
* @private
|
|
383
|
-
* @param {Store} projectStore
|
|
384
|
-
* @param {Object} options
|
|
385
|
-
* @returns {PhaseReceipt}
|
|
386
|
-
*/
|
|
387
|
-
function executeDomainInferencePhase(projectStore, options) {
|
|
388
|
-
const startTime = Date.now();
|
|
389
|
-
|
|
390
|
-
try {
|
|
391
|
-
// Create domain store from project store
|
|
392
|
-
const domainStore = createStore();
|
|
393
|
-
const baseIri = `${options.baseIri}domain#`;
|
|
394
|
-
|
|
395
|
-
// Extract entities from feature names and file patterns
|
|
396
|
-
const featureQuads = projectStore.getQuads(
|
|
397
|
-
null,
|
|
398
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
399
|
-
null
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
const entities = new Map();
|
|
403
|
-
|
|
404
|
-
for (const quad of featureQuads) {
|
|
405
|
-
const featureName = quad.object.value;
|
|
406
|
-
const entityName = inferEntityFromFeatureName(featureName);
|
|
407
|
-
|
|
408
|
-
if (entityName && !entities.has(entityName)) {
|
|
409
|
-
entities.set(entityName, { fields: [], source: featureName });
|
|
410
|
-
|
|
411
|
-
const entityIri = namedNode(`${baseIri}${encodeURIComponent(entityName)}`);
|
|
412
|
-
domainStore.addQuad(
|
|
413
|
-
entityIri,
|
|
414
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
415
|
-
namedNode('http://example.org/unrdf/domain#Entity')
|
|
416
|
-
);
|
|
417
|
-
domainStore.addQuad(
|
|
418
|
-
entityIri,
|
|
419
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
420
|
-
literal(entityName)
|
|
421
|
-
);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Infer common fields for entities
|
|
426
|
-
let totalFields = 0;
|
|
427
|
-
for (const [entityName, _entity] of entities) {
|
|
428
|
-
const fields = inferEntityFields(entityName);
|
|
429
|
-
totalFields += fields.length;
|
|
430
|
-
|
|
431
|
-
const entityIri = namedNode(`${baseIri}${encodeURIComponent(entityName)}`);
|
|
432
|
-
for (const field of fields) {
|
|
433
|
-
const fieldIri = namedNode(
|
|
434
|
-
`${baseIri}${encodeURIComponent(entityName)}/${encodeURIComponent(field.name)}`
|
|
435
|
-
);
|
|
436
|
-
domainStore.addQuad(
|
|
437
|
-
entityIri,
|
|
438
|
-
namedNode('http://example.org/unrdf/domain#hasField'),
|
|
439
|
-
fieldIri
|
|
440
|
-
);
|
|
441
|
-
domainStore.addQuad(
|
|
442
|
-
fieldIri,
|
|
443
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
444
|
-
literal(field.name)
|
|
445
|
-
);
|
|
446
|
-
domainStore.addQuad(
|
|
447
|
-
fieldIri,
|
|
448
|
-
namedNode('http://example.org/unrdf/domain#fieldType'),
|
|
449
|
-
literal(field.type)
|
|
450
|
-
);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
return {
|
|
455
|
-
duration: Date.now() - startTime,
|
|
456
|
-
success: true,
|
|
457
|
-
data: {
|
|
458
|
-
store: domainStore,
|
|
459
|
-
entities: entities.size,
|
|
460
|
-
fields: totalFields,
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
} catch (error) {
|
|
464
|
-
return {
|
|
465
|
-
duration: Date.now() - startTime,
|
|
466
|
-
success: false,
|
|
467
|
-
error: error.message,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Execute template inference phase
|
|
474
|
-
*
|
|
475
|
-
* @private
|
|
476
|
-
* @param {Store} domainStore
|
|
477
|
-
* @param {Object} options
|
|
478
|
-
* @returns {PhaseReceipt}
|
|
479
|
-
*/
|
|
480
|
-
function executeTemplateInferencePhase(domainStore, _options) {
|
|
481
|
-
const startTime = Date.now();
|
|
482
|
-
|
|
483
|
-
try {
|
|
484
|
-
const templateGraph = {
|
|
485
|
-
templates: [],
|
|
486
|
-
patterns: [],
|
|
487
|
-
};
|
|
488
|
-
|
|
489
|
-
// Infer templates from entities
|
|
490
|
-
const entityQuads = domainStore.getQuads(
|
|
491
|
-
null,
|
|
492
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
493
|
-
namedNode('http://example.org/unrdf/domain#Entity')
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
for (const quad of entityQuads) {
|
|
497
|
-
const _entityIri = quad.subject.value;
|
|
498
|
-
const labelQuads = domainStore.getQuads(
|
|
499
|
-
quad.subject,
|
|
500
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
501
|
-
null
|
|
502
|
-
);
|
|
503
|
-
const entityName = labelQuads[0]?.object.value || 'Unknown';
|
|
504
|
-
|
|
505
|
-
// Infer templates for each entity
|
|
506
|
-
const templates = inferTemplatesForEntity(entityName);
|
|
507
|
-
templateGraph.templates.push(...templates);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Infer patterns from template collection
|
|
511
|
-
const patterns = inferPatternsFromTemplates(templateGraph.templates);
|
|
512
|
-
templateGraph.patterns = patterns;
|
|
513
|
-
|
|
514
|
-
return {
|
|
515
|
-
duration: Date.now() - startTime,
|
|
516
|
-
success: true,
|
|
517
|
-
data: {
|
|
518
|
-
templateGraph,
|
|
519
|
-
templates: templateGraph.templates.length,
|
|
520
|
-
patterns: templateGraph.patterns,
|
|
521
|
-
},
|
|
522
|
-
};
|
|
523
|
-
} catch (error) {
|
|
524
|
-
return {
|
|
525
|
-
duration: Date.now() - startTime,
|
|
526
|
-
success: false,
|
|
527
|
-
error: error.message,
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Execute snapshot phase
|
|
534
|
-
*
|
|
535
|
-
* @private
|
|
536
|
-
* @param {InitializationState} state
|
|
537
|
-
* @param {Object} options
|
|
538
|
-
* @returns {PhaseReceipt}
|
|
539
|
-
*/
|
|
540
|
-
function executeSnapshotPhase(state, _options) {
|
|
541
|
-
const startTime = Date.now();
|
|
542
|
-
|
|
543
|
-
try {
|
|
544
|
-
const snapshot = {
|
|
545
|
-
hash: createSnapshotHash(state),
|
|
546
|
-
timestamp: new Date().toISOString(),
|
|
547
|
-
stores: {
|
|
548
|
-
fsStoreSize: state.fsStore?.size || 0,
|
|
549
|
-
projectStoreSize: state.projectStore?.size || 0,
|
|
550
|
-
domainStoreSize: state.domainStore?.size || 0,
|
|
551
|
-
},
|
|
552
|
-
templateCount: state.templateGraph?.templates?.length || 0,
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
return {
|
|
556
|
-
duration: Date.now() - startTime,
|
|
557
|
-
success: true,
|
|
558
|
-
data: {
|
|
559
|
-
snapshot,
|
|
560
|
-
hash: snapshot.hash,
|
|
561
|
-
timestamp: snapshot.timestamp,
|
|
562
|
-
},
|
|
563
|
-
};
|
|
564
|
-
} catch (error) {
|
|
565
|
-
return {
|
|
566
|
-
duration: Date.now() - startTime,
|
|
567
|
-
success: false,
|
|
568
|
-
error: error.message,
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Execute code complexity analysis phase
|
|
575
|
-
*
|
|
576
|
-
* @private
|
|
577
|
-
* @param {string} projectRoot
|
|
578
|
-
* @param {Store} [domainStore]
|
|
579
|
-
* @param {Object} options
|
|
580
|
-
* @returns {Promise<PhaseReceipt>}
|
|
581
|
-
*/
|
|
582
|
-
async function executeCodeComplexityPhase(projectRoot, domainStore, _options) {
|
|
583
|
-
const startTime = Date.now();
|
|
584
|
-
|
|
585
|
-
try {
|
|
586
|
-
const { store, summary } = await analyzeJsComplexity({
|
|
587
|
-
projectRoot,
|
|
588
|
-
baseStore: domainStore ? createStore(domainStore) : undefined,
|
|
589
|
-
mode: 'observe',
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
return {
|
|
593
|
-
duration: Date.now() - startTime,
|
|
594
|
-
success: true,
|
|
595
|
-
data: {
|
|
596
|
-
store,
|
|
597
|
-
summary,
|
|
598
|
-
filesAnalyzed: summary.filesAnalyzed,
|
|
599
|
-
averageComplexity: summary.averageCyclomatic,
|
|
600
|
-
topRisks: summary.topRisks?.length || 0,
|
|
601
|
-
},
|
|
602
|
-
};
|
|
603
|
-
} catch (error) {
|
|
604
|
-
return {
|
|
605
|
-
duration: Date.now() - startTime,
|
|
606
|
-
success: false,
|
|
607
|
-
error: error.message,
|
|
608
|
-
};
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Execute hooks registration phase
|
|
614
|
-
*
|
|
615
|
-
* @private
|
|
616
|
-
* @param {InitializationState} state
|
|
617
|
-
* @param {Object} options
|
|
618
|
-
* @returns {PhaseReceipt}
|
|
619
|
-
*/
|
|
620
|
-
function executeHooksPhase(state, _options) {
|
|
621
|
-
const startTime = Date.now();
|
|
622
|
-
|
|
623
|
-
try {
|
|
624
|
-
const hooks = [];
|
|
625
|
-
const invariants = [];
|
|
626
|
-
|
|
627
|
-
// Derive hooks from domain entities
|
|
628
|
-
const entityQuads = state.domainStore.getQuads(
|
|
629
|
-
null,
|
|
630
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
631
|
-
namedNode('http://example.org/unrdf/domain#Entity')
|
|
632
|
-
);
|
|
633
|
-
|
|
634
|
-
for (const quad of entityQuads) {
|
|
635
|
-
const labelQuads = state.domainStore.getQuads(
|
|
636
|
-
quad.subject,
|
|
637
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
638
|
-
null
|
|
639
|
-
);
|
|
640
|
-
const entityName = labelQuads[0]?.object.value || 'Unknown';
|
|
641
|
-
|
|
642
|
-
// Register validation hooks
|
|
643
|
-
hooks.push({
|
|
644
|
-
name: `validate${entityName}`,
|
|
645
|
-
type: 'validation',
|
|
646
|
-
entity: entityName,
|
|
647
|
-
trigger: 'pre-commit',
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// Register sync hooks
|
|
651
|
-
hooks.push({
|
|
652
|
-
name: `sync${entityName}ToStore`,
|
|
653
|
-
type: 'sync',
|
|
654
|
-
entity: entityName,
|
|
655
|
-
trigger: 'post-save',
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
// Register invariants
|
|
659
|
-
invariants.push({
|
|
660
|
-
name: `${entityName}RequiredFields`,
|
|
661
|
-
entity: entityName,
|
|
662
|
-
rule: 'all-required-fields-present',
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
return {
|
|
667
|
-
duration: Date.now() - startTime,
|
|
668
|
-
success: true,
|
|
669
|
-
data: {
|
|
670
|
-
registered: hooks.length,
|
|
671
|
-
invariants,
|
|
672
|
-
hooks,
|
|
673
|
-
},
|
|
674
|
-
};
|
|
675
|
-
} catch (error) {
|
|
676
|
-
return {
|
|
677
|
-
duration: Date.now() - startTime,
|
|
678
|
-
success: false,
|
|
679
|
-
error: error.message,
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Generate final report phase
|
|
686
|
-
*
|
|
687
|
-
* @private
|
|
688
|
-
* @param {InitializationState} state
|
|
689
|
-
* @param {Object} phases
|
|
690
|
-
* @param {Object} options
|
|
691
|
-
* @returns {PhaseReceipt}
|
|
692
|
-
*/
|
|
693
|
-
function generateReportPhase(state, phases, _options) {
|
|
694
|
-
const startTime = Date.now();
|
|
695
|
-
|
|
696
|
-
const report = {
|
|
697
|
-
summary: generateSummary(phases),
|
|
698
|
-
features: extractFeaturesList(state.projectStore),
|
|
699
|
-
stack: phases.stackDetection?.data?.profile || null,
|
|
700
|
-
entities: extractEntitiesList(state.domainStore),
|
|
701
|
-
templates: state.templateGraph?.templates || [],
|
|
702
|
-
hooks: phases.hooks?.data?.hooks || [],
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
return {
|
|
706
|
-
duration: Date.now() - startTime,
|
|
707
|
-
success: true,
|
|
708
|
-
data: {
|
|
709
|
-
report,
|
|
710
|
-
},
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* Build failure result
|
|
716
|
-
*
|
|
717
|
-
* @private
|
|
718
|
-
*/
|
|
719
|
-
function buildFailureResult(phases, startTime, failedPhase, errorMessage) {
|
|
720
|
-
return {
|
|
721
|
-
success: false,
|
|
722
|
-
receipt: {
|
|
723
|
-
phases,
|
|
724
|
-
totalDuration: Date.now() - startTime,
|
|
725
|
-
success: false,
|
|
726
|
-
failedPhase,
|
|
727
|
-
error: errorMessage,
|
|
728
|
-
},
|
|
729
|
-
report: {
|
|
730
|
-
summary: `Initialization failed at phase: ${failedPhase}`,
|
|
731
|
-
error: errorMessage,
|
|
732
|
-
},
|
|
733
|
-
state: {
|
|
734
|
-
fsStore: null,
|
|
735
|
-
projectStore: null,
|
|
736
|
-
domainStore: null,
|
|
737
|
-
templateGraph: null,
|
|
738
|
-
snapshot: null,
|
|
739
|
-
},
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
* Infer entity name from feature name
|
|
745
|
-
*
|
|
746
|
-
* @private
|
|
747
|
-
*/
|
|
748
|
-
function inferEntityFromFeatureName(featureName) {
|
|
749
|
-
// Skip common non-entity folders
|
|
750
|
-
const nonEntities = ['utils', 'helpers', 'lib', 'shared', 'common', 'config', 'hooks', 'types'];
|
|
751
|
-
if (nonEntities.includes(featureName.toLowerCase())) {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// Singularize common patterns
|
|
756
|
-
let entity = featureName;
|
|
757
|
-
if (entity.endsWith('s') && !entity.endsWith('ss')) {
|
|
758
|
-
entity = entity.slice(0, -1);
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
// PascalCase
|
|
762
|
-
return entity.charAt(0).toUpperCase() + entity.slice(1);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
/**
|
|
766
|
-
* Infer common fields for an entity
|
|
767
|
-
*
|
|
768
|
-
* @private
|
|
769
|
-
*/
|
|
770
|
-
function inferEntityFields(entityName) {
|
|
771
|
-
// Common fields for all entities
|
|
772
|
-
const commonFields = [
|
|
773
|
-
{ name: 'id', type: 'string' },
|
|
774
|
-
{ name: 'createdAt', type: 'datetime' },
|
|
775
|
-
{ name: 'updatedAt', type: 'datetime' },
|
|
776
|
-
];
|
|
777
|
-
|
|
778
|
-
// Entity-specific fields
|
|
779
|
-
const specificFields = {
|
|
780
|
-
User: [
|
|
781
|
-
{ name: 'email', type: 'string' },
|
|
782
|
-
{ name: 'name', type: 'string' },
|
|
783
|
-
{ name: 'role', type: 'string' },
|
|
784
|
-
],
|
|
785
|
-
Product: [
|
|
786
|
-
{ name: 'name', type: 'string' },
|
|
787
|
-
{ name: 'price', type: 'number' },
|
|
788
|
-
{ name: 'description', type: 'string' },
|
|
789
|
-
],
|
|
790
|
-
Order: [
|
|
791
|
-
{ name: 'status', type: 'string' },
|
|
792
|
-
{ name: 'total', type: 'number' },
|
|
793
|
-
{ name: 'items', type: 'array' },
|
|
794
|
-
],
|
|
795
|
-
Post: [
|
|
796
|
-
{ name: 'title', type: 'string' },
|
|
797
|
-
{ name: 'content', type: 'string' },
|
|
798
|
-
{ name: 'author', type: 'reference' },
|
|
799
|
-
],
|
|
800
|
-
};
|
|
801
|
-
|
|
802
|
-
return [...commonFields, ...(specificFields[entityName] || [])];
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
/**
|
|
806
|
-
* Infer templates for an entity
|
|
807
|
-
*
|
|
808
|
-
* @private
|
|
809
|
-
*/
|
|
810
|
-
function inferTemplatesForEntity(entityName) {
|
|
811
|
-
return [
|
|
812
|
-
{ name: `${entityName}Component`, type: 'component', entity: entityName },
|
|
813
|
-
{ name: `${entityName}Form`, type: 'form', entity: entityName },
|
|
814
|
-
{ name: `${entityName}List`, type: 'list', entity: entityName },
|
|
815
|
-
{ name: `${entityName}Service`, type: 'service', entity: entityName },
|
|
816
|
-
{ name: `${entityName}Schema`, type: 'schema', entity: entityName },
|
|
817
|
-
];
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
/**
|
|
821
|
-
* Infer patterns from templates
|
|
822
|
-
*
|
|
823
|
-
* @private
|
|
824
|
-
*/
|
|
825
|
-
function inferPatternsFromTemplates(templates) {
|
|
826
|
-
const patterns = [];
|
|
827
|
-
const templateTypes = new Set(templates.map(t => t.type));
|
|
828
|
-
|
|
829
|
-
if (templateTypes.has('component') && templateTypes.has('form')) {
|
|
830
|
-
patterns.push('crud-ui');
|
|
831
|
-
}
|
|
832
|
-
if (templateTypes.has('service') && templateTypes.has('schema')) {
|
|
833
|
-
patterns.push('api-first');
|
|
834
|
-
}
|
|
835
|
-
if (templateTypes.has('list')) {
|
|
836
|
-
patterns.push('data-table');
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
return patterns;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Create hash for snapshot
|
|
844
|
-
*
|
|
845
|
-
* @private
|
|
846
|
-
*/
|
|
847
|
-
function createSnapshotHash(state) {
|
|
848
|
-
const hash = createHash('sha256');
|
|
849
|
-
|
|
850
|
-
if (state.fsStore) hash.update(String(state.fsStore.size));
|
|
851
|
-
if (state.projectStore) hash.update(String(state.projectStore.size));
|
|
852
|
-
if (state.domainStore) hash.update(String(state.domainStore.size));
|
|
853
|
-
if (state.templateGraph) hash.update(String(state.templateGraph.templates?.length || 0));
|
|
854
|
-
|
|
855
|
-
hash.update(new Date().toISOString());
|
|
856
|
-
|
|
857
|
-
return hash.digest('hex').substring(0, 16);
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
/**
|
|
861
|
-
* Generate summary from phases
|
|
862
|
-
*
|
|
863
|
-
* @private
|
|
864
|
-
*/
|
|
865
|
-
function generateSummary(phases) {
|
|
866
|
-
const parts = [];
|
|
867
|
-
|
|
868
|
-
if (phases.scan?.success) {
|
|
869
|
-
parts.push(`Scanned ${phases.scan.data.files} files in ${phases.scan.data.folders} folders`);
|
|
870
|
-
}
|
|
871
|
-
if (phases.stackDetection?.success) {
|
|
872
|
-
const frameworks = phases.stackDetection.data.frameworks;
|
|
873
|
-
if (frameworks.length > 0) {
|
|
874
|
-
parts.push(`Detected stack: ${frameworks.join(', ')}`);
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
if (phases.projectModel?.success) {
|
|
878
|
-
parts.push(`Found ${phases.projectModel.data.features} features`);
|
|
879
|
-
}
|
|
880
|
-
if (phases.fileRoles?.success) {
|
|
881
|
-
parts.push(`Classified ${phases.fileRoles.data.classified} files`);
|
|
882
|
-
}
|
|
883
|
-
if (phases.domainInference?.success) {
|
|
884
|
-
parts.push(
|
|
885
|
-
`Inferred ${phases.domainInference.data.entities} entities with ${phases.domainInference.data.fields} fields`
|
|
886
|
-
);
|
|
887
|
-
}
|
|
888
|
-
if (phases.templateInference?.success) {
|
|
889
|
-
parts.push(`Generated ${phases.templateInference.data.templates} templates`);
|
|
890
|
-
}
|
|
891
|
-
if (phases.hooks?.success) {
|
|
892
|
-
parts.push(`Registered ${phases.hooks.data.registered} hooks`);
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
return parts.join('. ') + '.';
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
/**
|
|
899
|
-
* Extract features list from project store
|
|
900
|
-
*
|
|
901
|
-
* @private
|
|
902
|
-
*/
|
|
903
|
-
function extractFeaturesList(projectStore) {
|
|
904
|
-
if (!projectStore) return [];
|
|
905
|
-
|
|
906
|
-
const features = [];
|
|
907
|
-
const labelQuads = projectStore.getQuads(
|
|
908
|
-
null,
|
|
909
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
910
|
-
null
|
|
911
|
-
);
|
|
912
|
-
|
|
913
|
-
for (const quad of labelQuads) {
|
|
914
|
-
// Check if this is a feature
|
|
915
|
-
const typeQuads = projectStore.getQuads(
|
|
916
|
-
quad.subject,
|
|
917
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
918
|
-
namedNode('http://example.org/unrdf/project#Feature')
|
|
919
|
-
);
|
|
920
|
-
|
|
921
|
-
if (typeQuads.length > 0) {
|
|
922
|
-
features.push(quad.object.value);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
return features;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
/**
|
|
930
|
-
* Extract entities list from domain store
|
|
931
|
-
*
|
|
932
|
-
* @private
|
|
933
|
-
*/
|
|
934
|
-
function extractEntitiesList(domainStore) {
|
|
935
|
-
if (!domainStore) return [];
|
|
936
|
-
|
|
937
|
-
const entities = [];
|
|
938
|
-
const entityQuads = domainStore.getQuads(
|
|
939
|
-
null,
|
|
940
|
-
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
|
|
941
|
-
namedNode('http://example.org/unrdf/domain#Entity')
|
|
942
|
-
);
|
|
943
|
-
|
|
944
|
-
for (const quad of entityQuads) {
|
|
945
|
-
const labelQuads = domainStore.getQuads(
|
|
946
|
-
quad.subject,
|
|
947
|
-
namedNode('http://www.w3.org/2000/01/rdf-schema#label'),
|
|
948
|
-
null
|
|
949
|
-
);
|
|
950
|
-
|
|
951
|
-
if (labelQuads.length > 0) {
|
|
952
|
-
entities.push(labelQuads[0].object.value);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
return entities;
|
|
957
|
-
}
|