@principal-ai/principal-view-core 0.5.6
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/README.md +126 -0
- package/dist/ConfigurationLoader.d.ts +76 -0
- package/dist/ConfigurationLoader.d.ts.map +1 -0
- package/dist/ConfigurationLoader.js +144 -0
- package/dist/ConfigurationLoader.js.map +1 -0
- package/dist/ConfigurationValidator.d.ts +31 -0
- package/dist/ConfigurationValidator.d.ts.map +1 -0
- package/dist/ConfigurationValidator.js +242 -0
- package/dist/ConfigurationValidator.js.map +1 -0
- package/dist/EventProcessor.d.ts +49 -0
- package/dist/EventProcessor.d.ts.map +1 -0
- package/dist/EventProcessor.js +215 -0
- package/dist/EventProcessor.js.map +1 -0
- package/dist/EventRecorderService.d.ts +305 -0
- package/dist/EventRecorderService.d.ts.map +1 -0
- package/dist/EventRecorderService.js +463 -0
- package/dist/EventRecorderService.js.map +1 -0
- package/dist/LibraryLoader.d.ts +63 -0
- package/dist/LibraryLoader.d.ts.map +1 -0
- package/dist/LibraryLoader.js +188 -0
- package/dist/LibraryLoader.js.map +1 -0
- package/dist/PathBasedEventProcessor.d.ts +90 -0
- package/dist/PathBasedEventProcessor.d.ts.map +1 -0
- package/dist/PathBasedEventProcessor.js +239 -0
- package/dist/PathBasedEventProcessor.js.map +1 -0
- package/dist/SessionManager.d.ts +194 -0
- package/dist/SessionManager.d.ts.map +1 -0
- package/dist/SessionManager.js +299 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/ValidationEngine.d.ts +31 -0
- package/dist/ValidationEngine.d.ts.map +1 -0
- package/dist/ValidationEngine.js +158 -0
- package/dist/ValidationEngine.js.map +1 -0
- package/dist/helpers/GraphInstrumentationHelper.d.ts +93 -0
- package/dist/helpers/GraphInstrumentationHelper.d.ts.map +1 -0
- package/dist/helpers/GraphInstrumentationHelper.js +248 -0
- package/dist/helpers/GraphInstrumentationHelper.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/config.d.ts +57 -0
- package/dist/rules/config.d.ts.map +1 -0
- package/dist/rules/config.js +382 -0
- package/dist/rules/config.js.map +1 -0
- package/dist/rules/engine.d.ts +70 -0
- package/dist/rules/engine.d.ts.map +1 -0
- package/dist/rules/engine.js +252 -0
- package/dist/rules/engine.js.map +1 -0
- package/dist/rules/implementations/connection-type-references.d.ts +7 -0
- package/dist/rules/implementations/connection-type-references.d.ts.map +1 -0
- package/dist/rules/implementations/connection-type-references.js +104 -0
- package/dist/rules/implementations/connection-type-references.js.map +1 -0
- package/dist/rules/implementations/dead-end-states.d.ts +17 -0
- package/dist/rules/implementations/dead-end-states.d.ts.map +1 -0
- package/dist/rules/implementations/dead-end-states.js +72 -0
- package/dist/rules/implementations/dead-end-states.js.map +1 -0
- package/dist/rules/implementations/index.d.ts +24 -0
- package/dist/rules/implementations/index.d.ts.map +1 -0
- package/dist/rules/implementations/index.js +62 -0
- package/dist/rules/implementations/index.js.map +1 -0
- package/dist/rules/implementations/library-node-type-match.d.ts +17 -0
- package/dist/rules/implementations/library-node-type-match.d.ts.map +1 -0
- package/dist/rules/implementations/library-node-type-match.js +123 -0
- package/dist/rules/implementations/library-node-type-match.js.map +1 -0
- package/dist/rules/implementations/minimum-node-sources.d.ts +22 -0
- package/dist/rules/implementations/minimum-node-sources.d.ts.map +1 -0
- package/dist/rules/implementations/minimum-node-sources.js +54 -0
- package/dist/rules/implementations/minimum-node-sources.js.map +1 -0
- package/dist/rules/implementations/no-unknown-fields.d.ts +7 -0
- package/dist/rules/implementations/no-unknown-fields.d.ts.map +1 -0
- package/dist/rules/implementations/no-unknown-fields.js +211 -0
- package/dist/rules/implementations/no-unknown-fields.js.map +1 -0
- package/dist/rules/implementations/orphaned-edge-types.d.ts +7 -0
- package/dist/rules/implementations/orphaned-edge-types.d.ts.map +1 -0
- package/dist/rules/implementations/orphaned-edge-types.js +47 -0
- package/dist/rules/implementations/orphaned-edge-types.js.map +1 -0
- package/dist/rules/implementations/orphaned-node-types.d.ts +7 -0
- package/dist/rules/implementations/orphaned-node-types.d.ts.map +1 -0
- package/dist/rules/implementations/orphaned-node-types.js +50 -0
- package/dist/rules/implementations/orphaned-node-types.js.map +1 -0
- package/dist/rules/implementations/required-metadata.d.ts +7 -0
- package/dist/rules/implementations/required-metadata.d.ts.map +1 -0
- package/dist/rules/implementations/required-metadata.js +57 -0
- package/dist/rules/implementations/required-metadata.js.map +1 -0
- package/dist/rules/implementations/state-transition-references.d.ts +7 -0
- package/dist/rules/implementations/state-transition-references.d.ts.map +1 -0
- package/dist/rules/implementations/state-transition-references.js +135 -0
- package/dist/rules/implementations/state-transition-references.js.map +1 -0
- package/dist/rules/implementations/unreachable-states.d.ts +7 -0
- package/dist/rules/implementations/unreachable-states.d.ts.map +1 -0
- package/dist/rules/implementations/unreachable-states.js +80 -0
- package/dist/rules/implementations/unreachable-states.js.map +1 -0
- package/dist/rules/implementations/valid-action-patterns.d.ts +17 -0
- package/dist/rules/implementations/valid-action-patterns.d.ts.map +1 -0
- package/dist/rules/implementations/valid-action-patterns.js +109 -0
- package/dist/rules/implementations/valid-action-patterns.js.map +1 -0
- package/dist/rules/implementations/valid-color-format.d.ts +7 -0
- package/dist/rules/implementations/valid-color-format.d.ts.map +1 -0
- package/dist/rules/implementations/valid-color-format.js +91 -0
- package/dist/rules/implementations/valid-color-format.js.map +1 -0
- package/dist/rules/implementations/valid-edge-types.d.ts +7 -0
- package/dist/rules/implementations/valid-edge-types.d.ts.map +1 -0
- package/dist/rules/implementations/valid-edge-types.js +244 -0
- package/dist/rules/implementations/valid-edge-types.js.map +1 -0
- package/dist/rules/implementations/valid-node-types.d.ts +7 -0
- package/dist/rules/implementations/valid-node-types.d.ts.map +1 -0
- package/dist/rules/implementations/valid-node-types.js +175 -0
- package/dist/rules/implementations/valid-node-types.js.map +1 -0
- package/dist/rules/index.d.ts +28 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +45 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/types.d.ts +309 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +35 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/types/canvas.d.ts +409 -0
- package/dist/types/canvas.d.ts.map +1 -0
- package/dist/types/canvas.js +70 -0
- package/dist/types/canvas.js.map +1 -0
- package/dist/types/index.d.ts +311 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/library.d.ts +185 -0
- package/dist/types/library.d.ts.map +1 -0
- package/dist/types/library.js +15 -0
- package/dist/types/library.js.map +1 -0
- package/dist/types/path-based-config.d.ts +230 -0
- package/dist/types/path-based-config.d.ts.map +1 -0
- package/dist/types/path-based-config.js +9 -0
- package/dist/types/path-based-config.js.map +1 -0
- package/dist/utils/CanvasConverter.d.ts +118 -0
- package/dist/utils/CanvasConverter.d.ts.map +1 -0
- package/dist/utils/CanvasConverter.js +315 -0
- package/dist/utils/CanvasConverter.js.map +1 -0
- package/dist/utils/GraphConverter.d.ts +18 -0
- package/dist/utils/GraphConverter.d.ts.map +1 -0
- package/dist/utils/GraphConverter.js +61 -0
- package/dist/utils/GraphConverter.js.map +1 -0
- package/dist/utils/LibraryConverter.d.ts +113 -0
- package/dist/utils/LibraryConverter.d.ts.map +1 -0
- package/dist/utils/LibraryConverter.js +166 -0
- package/dist/utils/LibraryConverter.js.map +1 -0
- package/dist/utils/PathMatcher.d.ts +55 -0
- package/dist/utils/PathMatcher.d.ts.map +1 -0
- package/dist/utils/PathMatcher.js +172 -0
- package/dist/utils/PathMatcher.js.map +1 -0
- package/dist/utils/YamlParser.d.ts +36 -0
- package/dist/utils/YamlParser.d.ts.map +1 -0
- package/dist/utils/YamlParser.js +63 -0
- package/dist/utils/YamlParser.js.map +1 -0
- package/package.json +47 -0
- package/src/ConfigurationLoader.test.ts +490 -0
- package/src/ConfigurationLoader.ts +185 -0
- package/src/ConfigurationValidator.test.ts +200 -0
- package/src/ConfigurationValidator.ts +283 -0
- package/src/EventProcessor.test.ts +405 -0
- package/src/EventProcessor.ts +250 -0
- package/src/EventRecorderService.test.ts +541 -0
- package/src/EventRecorderService.ts +744 -0
- package/src/LibraryLoader.ts +215 -0
- package/src/PathBasedEventProcessor.test.ts +567 -0
- package/src/PathBasedEventProcessor.ts +332 -0
- package/src/SessionManager.test.ts +424 -0
- package/src/SessionManager.ts +470 -0
- package/src/ValidationEngine.test.ts +371 -0
- package/src/ValidationEngine.ts +196 -0
- package/src/helpers/GraphInstrumentationHelper.test.ts +340 -0
- package/src/helpers/GraphInstrumentationHelper.ts +326 -0
- package/src/index.ts +85 -0
- package/src/rules/config.test.ts +278 -0
- package/src/rules/config.ts +459 -0
- package/src/rules/engine.test.ts +332 -0
- package/src/rules/engine.ts +318 -0
- package/src/rules/implementations/connection-type-references.ts +117 -0
- package/src/rules/implementations/dead-end-states.ts +101 -0
- package/src/rules/implementations/index.ts +73 -0
- package/src/rules/implementations/library-node-type-match.ts +148 -0
- package/src/rules/implementations/minimum-node-sources.ts +82 -0
- package/src/rules/implementations/no-unknown-fields.ts +342 -0
- package/src/rules/implementations/orphaned-edge-types.ts +55 -0
- package/src/rules/implementations/orphaned-node-types.ts +58 -0
- package/src/rules/implementations/required-metadata.ts +64 -0
- package/src/rules/implementations/state-transition-references.ts +151 -0
- package/src/rules/implementations/unreachable-states.ts +94 -0
- package/src/rules/implementations/valid-action-patterns.ts +136 -0
- package/src/rules/implementations/valid-color-format.ts +140 -0
- package/src/rules/implementations/valid-edge-types.ts +258 -0
- package/src/rules/implementations/valid-node-types.ts +189 -0
- package/src/rules/index.ts +95 -0
- package/src/rules/types.ts +426 -0
- package/src/types/canvas.ts +496 -0
- package/src/types/index.ts +382 -0
- package/src/types/library.ts +233 -0
- package/src/types/path-based-config.ts +281 -0
- package/src/utils/CanvasConverter.ts +431 -0
- package/src/utils/GraphConverter.test.ts +195 -0
- package/src/utils/GraphConverter.ts +71 -0
- package/src/utils/LibraryConverter.ts +245 -0
- package/src/utils/PathMatcher.test.ts +148 -0
- package/src/utils/PathMatcher.ts +183 -0
- package/src/utils/YamlParser.ts +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# @principal-ai/visual-validation-core
|
|
2
|
+
|
|
3
|
+
Core logic library for the Visual Validation Framework - **framework-agnostic**.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- š TypeScript type definitions for graph configurations and events
|
|
8
|
+
- āļø Event processing engine
|
|
9
|
+
- ā
Validation engine with rule checking
|
|
10
|
+
- š Graph state management
|
|
11
|
+
- š§Ŗ Test instrumentation helpers
|
|
12
|
+
|
|
13
|
+
**Zero UI dependencies** - can be used in Node.js, tests, or any JavaScript environment.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @principal-ai/visual-validation-core
|
|
19
|
+
# or
|
|
20
|
+
bun add @principal-ai/visual-validation-core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### Define Configuration
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import type { GraphConfiguration } from '@principal-ai/visual-validation-core';
|
|
29
|
+
|
|
30
|
+
const config: GraphConfiguration = {
|
|
31
|
+
metadata: {
|
|
32
|
+
name: 'Control Tower',
|
|
33
|
+
version: '1.0.0',
|
|
34
|
+
},
|
|
35
|
+
nodeTypes: {
|
|
36
|
+
user: {
|
|
37
|
+
shape: 'circle',
|
|
38
|
+
color: '#4CAF50',
|
|
39
|
+
dataSchema: {
|
|
40
|
+
userId: { type: 'string', required: true },
|
|
41
|
+
},
|
|
42
|
+
states: {
|
|
43
|
+
online: { color: '#4CAF50' },
|
|
44
|
+
offline: { color: '#9E9E9E' },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
server: {
|
|
48
|
+
shape: 'hexagon',
|
|
49
|
+
color: '#9C27B0',
|
|
50
|
+
dataSchema: {
|
|
51
|
+
uptime: { type: 'number' },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
edgeTypes: {
|
|
56
|
+
connection: {
|
|
57
|
+
style: 'solid',
|
|
58
|
+
directed: true,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
allowedConnections: [
|
|
62
|
+
{ from: 'user', to: 'server', via: 'connection' },
|
|
63
|
+
],
|
|
64
|
+
validation: {
|
|
65
|
+
stateTransitions: {
|
|
66
|
+
user: [
|
|
67
|
+
{ from: 'offline', to: ['online'] },
|
|
68
|
+
{ from: 'online', to: ['offline'] },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Process Events
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { EventProcessor } from '@principal-ai/visual-validation-core';
|
|
79
|
+
|
|
80
|
+
const processor = new EventProcessor(config);
|
|
81
|
+
|
|
82
|
+
// Process events
|
|
83
|
+
const result = processor.processEvent({
|
|
84
|
+
id: 'evt-1',
|
|
85
|
+
type: 'user_connected',
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
category: 'node',
|
|
88
|
+
operation: 'create',
|
|
89
|
+
payload: {
|
|
90
|
+
operation: 'create',
|
|
91
|
+
nodeId: 'user-1',
|
|
92
|
+
nodeType: 'user',
|
|
93
|
+
data: { userId: 'alice' },
|
|
94
|
+
},
|
|
95
|
+
expected: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
console.log('Valid:', result.validation.valid);
|
|
99
|
+
console.log('Violations:', result.validation.violations);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Test Instrumentation
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { GraphInstrumentationHelper } from '@principal-ai/visual-validation-core';
|
|
106
|
+
|
|
107
|
+
describe('My system test', () => {
|
|
108
|
+
it('should handle user connections', () => {
|
|
109
|
+
const helper = new GraphInstrumentationHelper(config);
|
|
110
|
+
|
|
111
|
+
// Emit events from your test
|
|
112
|
+
helper.emitNodeCreated('server-1', 'server', { uptime: 0 });
|
|
113
|
+
helper.emitNodeCreated('user-alice', 'user', { userId: 'alice' });
|
|
114
|
+
helper.emitEdgeCreated('conn-1', 'connection', 'user-alice', 'server-1');
|
|
115
|
+
helper.emitStateChange('user-alice', 'online');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## API
|
|
121
|
+
|
|
122
|
+
See the main [README](../../README.md) for full documentation.
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader for managing multiple graph configurations
|
|
3
|
+
*
|
|
4
|
+
* Supports loading configurations from the .vgc/ folder using the adapter pattern
|
|
5
|
+
* for environment-agnostic file operations.
|
|
6
|
+
*/
|
|
7
|
+
import type { FileSystemAdapter } from '@principal-ai/repository-abstraction';
|
|
8
|
+
import type { PathBasedGraphConfiguration } from './types/path-based-config';
|
|
9
|
+
/**
|
|
10
|
+
* Represents a single configuration file
|
|
11
|
+
*/
|
|
12
|
+
export interface ConfigurationFile {
|
|
13
|
+
/** Configuration name (filename without extension) */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Full path to the configuration file */
|
|
16
|
+
path: string;
|
|
17
|
+
/** Parsed configuration data */
|
|
18
|
+
config: PathBasedGraphConfiguration;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Result of loading all configurations
|
|
22
|
+
*/
|
|
23
|
+
export interface ConfigurationLoadResult {
|
|
24
|
+
/** Successfully loaded configurations */
|
|
25
|
+
configs: ConfigurationFile[];
|
|
26
|
+
/** Errors encountered during loading */
|
|
27
|
+
errors: Array<{
|
|
28
|
+
file: string;
|
|
29
|
+
error: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loader for managing multiple graph configurations from .vgc/ folder
|
|
34
|
+
*/
|
|
35
|
+
export declare class ConfigurationLoader {
|
|
36
|
+
private fsAdapter;
|
|
37
|
+
private static readonly CONFIG_DIR;
|
|
38
|
+
constructor(fsAdapter: FileSystemAdapter);
|
|
39
|
+
/**
|
|
40
|
+
* Check if the .vgc/ configuration directory exists
|
|
41
|
+
*
|
|
42
|
+
* @param baseDir - Base directory to search from
|
|
43
|
+
* @returns True if .vgc/ directory exists
|
|
44
|
+
*/
|
|
45
|
+
hasConfigDirectory(baseDir: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* List all available configuration names in .vgc/ folder
|
|
48
|
+
*
|
|
49
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
50
|
+
* @returns Array of configuration names (without extensions)
|
|
51
|
+
*/
|
|
52
|
+
listConfigurations(baseDir: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* Load a specific configuration by name
|
|
55
|
+
*
|
|
56
|
+
* @param name - Configuration name (without extension)
|
|
57
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
58
|
+
* @returns Configuration file or null if not found/invalid
|
|
59
|
+
*/
|
|
60
|
+
loadByName(name: string, baseDir: string): ConfigurationFile | null;
|
|
61
|
+
/**
|
|
62
|
+
* Load all configurations from .vgc/ folder
|
|
63
|
+
*
|
|
64
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
65
|
+
* @returns Result containing all loaded configs and any errors
|
|
66
|
+
*/
|
|
67
|
+
loadAll(baseDir: string): ConfigurationLoadResult;
|
|
68
|
+
/**
|
|
69
|
+
* Get the configuration directory path
|
|
70
|
+
*
|
|
71
|
+
* @param baseDir - Base directory
|
|
72
|
+
* @returns Full path to .vgc/ directory
|
|
73
|
+
*/
|
|
74
|
+
getConfigDirectoryPath(baseDir: string): string;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=ConfigurationLoader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationLoader.d.ts","sourceRoot":"","sources":["../src/ConfigurationLoader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAG7E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,MAAM,EAAE,2BAA2B,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,yCAAyC;IACzC,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,wCAAwC;IACxC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,SAAS;IAF7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAU;gBAExB,SAAS,EAAE,iBAAiB;IAEhD;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAK5C;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IAc7C;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAkCnE;;;;;OAKG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB;IAwDjD;;;;;OAKG;IACH,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;CAGhD"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader for managing multiple graph configurations
|
|
3
|
+
*
|
|
4
|
+
* Supports loading configurations from the .vgc/ folder using the adapter pattern
|
|
5
|
+
* for environment-agnostic file operations.
|
|
6
|
+
*/
|
|
7
|
+
import { parseYaml, isYamlFile, getConfigNameFromFilename } from './utils/YamlParser';
|
|
8
|
+
/**
|
|
9
|
+
* Loader for managing multiple graph configurations from .vgc/ folder
|
|
10
|
+
*/
|
|
11
|
+
export class ConfigurationLoader {
|
|
12
|
+
constructor(fsAdapter) {
|
|
13
|
+
this.fsAdapter = fsAdapter;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if the .vgc/ configuration directory exists
|
|
17
|
+
*
|
|
18
|
+
* @param baseDir - Base directory to search from
|
|
19
|
+
* @returns True if .vgc/ directory exists
|
|
20
|
+
*/
|
|
21
|
+
hasConfigDirectory(baseDir) {
|
|
22
|
+
const configPath = this.fsAdapter.join(baseDir, ConfigurationLoader.CONFIG_DIR);
|
|
23
|
+
return this.fsAdapter.exists(configPath) && this.fsAdapter.isDirectory(configPath);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* List all available configuration names in .vgc/ folder
|
|
27
|
+
*
|
|
28
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
29
|
+
* @returns Array of configuration names (without extensions)
|
|
30
|
+
*/
|
|
31
|
+
listConfigurations(baseDir) {
|
|
32
|
+
if (!this.hasConfigDirectory(baseDir)) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const configPath = this.fsAdapter.join(baseDir, ConfigurationLoader.CONFIG_DIR);
|
|
36
|
+
const files = this.fsAdapter.readDir(configPath);
|
|
37
|
+
return files
|
|
38
|
+
.filter(isYamlFile)
|
|
39
|
+
.map(getConfigNameFromFilename)
|
|
40
|
+
.sort();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load a specific configuration by name
|
|
44
|
+
*
|
|
45
|
+
* @param name - Configuration name (without extension)
|
|
46
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
47
|
+
* @returns Configuration file or null if not found/invalid
|
|
48
|
+
*/
|
|
49
|
+
loadByName(name, baseDir) {
|
|
50
|
+
if (!this.hasConfigDirectory(baseDir)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const configPath = this.fsAdapter.join(baseDir, ConfigurationLoader.CONFIG_DIR);
|
|
54
|
+
// Try both .yaml and .yml extensions
|
|
55
|
+
for (const ext of ['yaml', 'yml']) {
|
|
56
|
+
const filename = `${name}.${ext}`;
|
|
57
|
+
const fullPath = this.fsAdapter.join(configPath, filename);
|
|
58
|
+
if (this.fsAdapter.exists(fullPath)) {
|
|
59
|
+
try {
|
|
60
|
+
const content = this.fsAdapter.readFile(fullPath);
|
|
61
|
+
const parseResult = parseYaml(content, filename);
|
|
62
|
+
if (parseResult.success && parseResult.data) {
|
|
63
|
+
return {
|
|
64
|
+
name,
|
|
65
|
+
path: fullPath,
|
|
66
|
+
config: parseResult.data,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// File exists but couldn't be read or parsed
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Load all configurations from .vgc/ folder
|
|
80
|
+
*
|
|
81
|
+
* @param baseDir - Base directory containing .vgc/ folder
|
|
82
|
+
* @returns Result containing all loaded configs and any errors
|
|
83
|
+
*/
|
|
84
|
+
loadAll(baseDir) {
|
|
85
|
+
const result = {
|
|
86
|
+
configs: [],
|
|
87
|
+
errors: [],
|
|
88
|
+
};
|
|
89
|
+
if (!this.hasConfigDirectory(baseDir)) {
|
|
90
|
+
result.errors.push({
|
|
91
|
+
file: '.vgc',
|
|
92
|
+
error: 'Configuration directory .vgc/ not found',
|
|
93
|
+
});
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
const configPath = this.fsAdapter.join(baseDir, ConfigurationLoader.CONFIG_DIR);
|
|
97
|
+
const files = this.fsAdapter.readDir(configPath);
|
|
98
|
+
for (const filename of files) {
|
|
99
|
+
if (!isYamlFile(filename)) {
|
|
100
|
+
continue; // Skip non-YAML files
|
|
101
|
+
}
|
|
102
|
+
const fullPath = this.fsAdapter.join(configPath, filename);
|
|
103
|
+
const name = getConfigNameFromFilename(filename);
|
|
104
|
+
try {
|
|
105
|
+
const content = this.fsAdapter.readFile(fullPath);
|
|
106
|
+
const parseResult = parseYaml(content, filename);
|
|
107
|
+
if (parseResult.success && parseResult.data) {
|
|
108
|
+
result.configs.push({
|
|
109
|
+
name,
|
|
110
|
+
path: fullPath,
|
|
111
|
+
config: parseResult.data,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (parseResult.error) {
|
|
115
|
+
result.errors.push({
|
|
116
|
+
file: filename,
|
|
117
|
+
error: parseResult.error,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
123
|
+
result.errors.push({
|
|
124
|
+
file: filename,
|
|
125
|
+
error: `Failed to read file: ${errorMessage}`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Sort configs by name for consistent ordering
|
|
130
|
+
result.configs.sort((a, b) => a.name.localeCompare(b.name));
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get the configuration directory path
|
|
135
|
+
*
|
|
136
|
+
* @param baseDir - Base directory
|
|
137
|
+
* @returns Full path to .vgc/ directory
|
|
138
|
+
*/
|
|
139
|
+
getConfigDirectoryPath(baseDir) {
|
|
140
|
+
return this.fsAdapter.join(baseDir, ConfigurationLoader.CONFIG_DIR);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
ConfigurationLoader.CONFIG_DIR = '.vgc';
|
|
144
|
+
//# sourceMappingURL=ConfigurationLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationLoader.js","sourceRoot":"","sources":["../src/ConfigurationLoader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { GraphConfiguration } from './types';
|
|
2
|
+
export interface ConfigurationValidationError {
|
|
3
|
+
type: 'error' | 'warning';
|
|
4
|
+
message: string;
|
|
5
|
+
path: string;
|
|
6
|
+
suggestion?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ConfigurationValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
errors: ConfigurationValidationError[];
|
|
11
|
+
warnings: ConfigurationValidationError[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Validates GraphConfiguration to catch issues early
|
|
15
|
+
* Use this before creating EventProcessor or rendering graphs
|
|
16
|
+
*/
|
|
17
|
+
export declare class ConfigurationValidator {
|
|
18
|
+
/**
|
|
19
|
+
* Validate a GraphConfiguration
|
|
20
|
+
*/
|
|
21
|
+
static validate(config: GraphConfiguration): ConfigurationValidationResult;
|
|
22
|
+
/**
|
|
23
|
+
* Validate and throw if invalid
|
|
24
|
+
*/
|
|
25
|
+
static validateOrThrow(config: GraphConfiguration): void;
|
|
26
|
+
/**
|
|
27
|
+
* Get a formatted report of validation results
|
|
28
|
+
*/
|
|
29
|
+
static formatReport(result: ConfigurationValidationResult): string;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=ConfigurationValidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationValidator.d.ts","sourceRoot":"","sources":["../src/ConfigurationValidator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,4BAA4B,EAAE,CAAC;IACvC,QAAQ,EAAE,4BAA4B,EAAE,CAAC;CAC1C;AAED;;;GAGG;AACH,qBAAa,sBAAsB;IACjC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,6BAA6B;IAqM1E;;OAEG;IACH,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAyBxD;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,6BAA6B,GAAG,MAAM;CA+BnE"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates GraphConfiguration to catch issues early
|
|
3
|
+
* Use this before creating EventProcessor or rendering graphs
|
|
4
|
+
*/
|
|
5
|
+
export class ConfigurationValidator {
|
|
6
|
+
/**
|
|
7
|
+
* Validate a GraphConfiguration
|
|
8
|
+
*/
|
|
9
|
+
static validate(config) {
|
|
10
|
+
const errors = [];
|
|
11
|
+
const warnings = [];
|
|
12
|
+
// Validate metadata
|
|
13
|
+
if (!config.metadata?.name) {
|
|
14
|
+
errors.push({
|
|
15
|
+
type: 'error',
|
|
16
|
+
message: 'Configuration must have a name',
|
|
17
|
+
path: 'metadata.name',
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
if (!config.metadata?.version) {
|
|
21
|
+
errors.push({
|
|
22
|
+
type: 'error',
|
|
23
|
+
message: 'Configuration must have a version',
|
|
24
|
+
path: 'metadata.version',
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Validate nodeTypes
|
|
28
|
+
if (!config.nodeTypes || Object.keys(config.nodeTypes).length === 0) {
|
|
29
|
+
errors.push({
|
|
30
|
+
type: 'error',
|
|
31
|
+
message: 'Configuration must define at least one node type',
|
|
32
|
+
path: 'nodeTypes',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Validate each node type
|
|
37
|
+
for (const [typeName, nodeType] of Object.entries(config.nodeTypes)) {
|
|
38
|
+
if (!nodeType.shape) {
|
|
39
|
+
errors.push({
|
|
40
|
+
type: 'error',
|
|
41
|
+
message: `Node type '${typeName}' must have a shape`,
|
|
42
|
+
path: `nodeTypes.${typeName}.shape`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (!nodeType.dataSchema || Object.keys(nodeType.dataSchema).length === 0) {
|
|
46
|
+
warnings.push({
|
|
47
|
+
type: 'warning',
|
|
48
|
+
message: `Node type '${typeName}' has no data schema defined`,
|
|
49
|
+
path: `nodeTypes.${typeName}.dataSchema`,
|
|
50
|
+
suggestion: 'Consider defining data schema for better type safety',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Validate states if defined
|
|
54
|
+
if (nodeType.states) {
|
|
55
|
+
for (const [stateName, state] of Object.entries(nodeType.states)) {
|
|
56
|
+
if (!state.label && !state.color && !state.icon) {
|
|
57
|
+
warnings.push({
|
|
58
|
+
type: 'warning',
|
|
59
|
+
message: `State '${stateName}' of node type '${typeName}' has no visual properties`,
|
|
60
|
+
path: `nodeTypes.${typeName}.states.${stateName}`,
|
|
61
|
+
suggestion: 'Add at least one of: label, color, or icon',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Validate edgeTypes
|
|
69
|
+
if (!config.edgeTypes || Object.keys(config.edgeTypes).length === 0) {
|
|
70
|
+
errors.push({
|
|
71
|
+
type: 'error',
|
|
72
|
+
message: 'Configuration must define at least one edge type',
|
|
73
|
+
path: 'edgeTypes',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Validate each edge type
|
|
78
|
+
for (const [typeName, edgeType] of Object.entries(config.edgeTypes)) {
|
|
79
|
+
if (!edgeType.style) {
|
|
80
|
+
errors.push({
|
|
81
|
+
type: 'error',
|
|
82
|
+
message: `Edge type '${typeName}' must have a style`,
|
|
83
|
+
path: `edgeTypes.${typeName}.style`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Validate allowedConnections
|
|
89
|
+
if (!config.allowedConnections || config.allowedConnections.length === 0) {
|
|
90
|
+
warnings.push({
|
|
91
|
+
type: 'warning',
|
|
92
|
+
message: 'No connection rules defined',
|
|
93
|
+
path: 'allowedConnections',
|
|
94
|
+
suggestion: 'Define connection rules to validate graph structure',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const nodeTypeNames = Object.keys(config.nodeTypes);
|
|
99
|
+
const edgeTypeNames = Object.keys(config.edgeTypes);
|
|
100
|
+
config.allowedConnections.forEach((rule, index) => {
|
|
101
|
+
// Check if 'from' node type exists
|
|
102
|
+
if (!nodeTypeNames.includes(rule.from)) {
|
|
103
|
+
errors.push({
|
|
104
|
+
type: 'error',
|
|
105
|
+
message: `Connection rule references undefined node type '${rule.from}'`,
|
|
106
|
+
path: `allowedConnections[${index}].from`,
|
|
107
|
+
suggestion: `Available node types: ${nodeTypeNames.join(', ')}`,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Check if 'to' node type exists
|
|
111
|
+
if (!nodeTypeNames.includes(rule.to)) {
|
|
112
|
+
errors.push({
|
|
113
|
+
type: 'error',
|
|
114
|
+
message: `Connection rule references undefined node type '${rule.to}'`,
|
|
115
|
+
path: `allowedConnections[${index}].to`,
|
|
116
|
+
suggestion: `Available node types: ${nodeTypeNames.join(', ')}`,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Check if 'via' edge type exists
|
|
120
|
+
if (!edgeTypeNames.includes(rule.via)) {
|
|
121
|
+
errors.push({
|
|
122
|
+
type: 'error',
|
|
123
|
+
message: `Connection rule references undefined edge type '${rule.via}'`,
|
|
124
|
+
path: `allowedConnections[${index}].via`,
|
|
125
|
+
suggestion: `Available edge types: ${edgeTypeNames.join(', ')}`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// Validate validation rules if present
|
|
131
|
+
if (config.validation) {
|
|
132
|
+
// Validate state transitions
|
|
133
|
+
if (config.validation.stateTransitions) {
|
|
134
|
+
const nodeTypeNames = Object.keys(config.nodeTypes);
|
|
135
|
+
for (const [nodeType, transitions] of Object.entries(config.validation.stateTransitions)) {
|
|
136
|
+
if (!nodeTypeNames.includes(nodeType)) {
|
|
137
|
+
errors.push({
|
|
138
|
+
type: 'error',
|
|
139
|
+
message: `State transition rule references undefined node type '${nodeType}'`,
|
|
140
|
+
path: `validation.stateTransitions.${nodeType}`,
|
|
141
|
+
suggestion: `Available node types: ${nodeTypeNames.join(', ')}`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Check if states exist in node type definition
|
|
146
|
+
const nodeTypeDef = config.nodeTypes[nodeType];
|
|
147
|
+
const definedStates = nodeTypeDef.states ? Object.keys(nodeTypeDef.states) : [];
|
|
148
|
+
transitions.forEach((transition, index) => {
|
|
149
|
+
if (definedStates.length > 0 && !definedStates.includes(transition.from)) {
|
|
150
|
+
warnings.push({
|
|
151
|
+
type: 'warning',
|
|
152
|
+
message: `State transition references undefined state '${transition.from}' for node type '${nodeType}'`,
|
|
153
|
+
path: `validation.stateTransitions.${nodeType}[${index}].from`,
|
|
154
|
+
suggestion: `Defined states: ${definedStates.join(', ')}`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
transition.to.forEach((toState) => {
|
|
158
|
+
if (definedStates.length > 0 && !definedStates.includes(toState)) {
|
|
159
|
+
warnings.push({
|
|
160
|
+
type: 'warning',
|
|
161
|
+
message: `State transition references undefined state '${toState}' for node type '${nodeType}'`,
|
|
162
|
+
path: `validation.stateTransitions.${nodeType}[${index}].to`,
|
|
163
|
+
suggestion: `Defined states: ${definedStates.join(', ')}`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Validate cardinality rules
|
|
172
|
+
if (config.validation.cardinality) {
|
|
173
|
+
const nodeTypeNames = Object.keys(config.nodeTypes);
|
|
174
|
+
for (const nodeType of Object.keys(config.validation.cardinality)) {
|
|
175
|
+
if (!nodeTypeNames.includes(nodeType)) {
|
|
176
|
+
errors.push({
|
|
177
|
+
type: 'error',
|
|
178
|
+
message: `Cardinality rule references undefined node type '${nodeType}'`,
|
|
179
|
+
path: `validation.cardinality.${nodeType}`,
|
|
180
|
+
suggestion: `Available node types: ${nodeTypeNames.join(', ')}`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: errors.length === 0,
|
|
188
|
+
errors,
|
|
189
|
+
warnings,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Validate and throw if invalid
|
|
194
|
+
*/
|
|
195
|
+
static validateOrThrow(config) {
|
|
196
|
+
const result = this.validate(config);
|
|
197
|
+
if (!result.valid) {
|
|
198
|
+
const errorMessages = result.errors.map((e) => ` - ${e.path}: ${e.message}${e.suggestion ? ` (${e.suggestion})` : ''}`);
|
|
199
|
+
throw new Error(`Invalid GraphConfiguration:\n${errorMessages.join('\n')}\n\n` +
|
|
200
|
+
`Found ${result.errors.length} error(s) and ${result.warnings.length} warning(s)`);
|
|
201
|
+
}
|
|
202
|
+
// Log warnings
|
|
203
|
+
if (result.warnings.length > 0) {
|
|
204
|
+
console.warn(`Configuration has ${result.warnings.length} warning(s):`);
|
|
205
|
+
result.warnings.forEach((w) => {
|
|
206
|
+
console.warn(` - ${w.path}: ${w.message}${w.suggestion ? ` (${w.suggestion})` : ''}`);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get a formatted report of validation results
|
|
212
|
+
*/
|
|
213
|
+
static formatReport(result) {
|
|
214
|
+
const lines = [];
|
|
215
|
+
if (result.valid) {
|
|
216
|
+
lines.push('ā
Configuration is valid');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
lines.push(`ā Configuration has ${result.errors.length} error(s)`);
|
|
220
|
+
}
|
|
221
|
+
if (result.errors.length > 0) {
|
|
222
|
+
lines.push('\nErrors:');
|
|
223
|
+
result.errors.forEach((e) => {
|
|
224
|
+
lines.push(` ā ${e.path}: ${e.message}`);
|
|
225
|
+
if (e.suggestion) {
|
|
226
|
+
lines.push(` š” ${e.suggestion}`);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (result.warnings.length > 0) {
|
|
231
|
+
lines.push(`\nā ļø ${result.warnings.length} warning(s):`);
|
|
232
|
+
result.warnings.forEach((w) => {
|
|
233
|
+
lines.push(` ā ļø ${w.path}: ${w.message}`);
|
|
234
|
+
if (w.suggestion) {
|
|
235
|
+
lines.push(` š” ${w.suggestion}`);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return lines.join('\n');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=ConfigurationValidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfigurationValidator.js","sourceRoot":"","sources":["../src/ConfigurationValidator.ts"],"names":[],"mappings":"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"}
|