@soulcraft/brainy 3.17.0 → 3.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +58 -10
- package/dist/augmentations/defaultAugmentations.d.ts +0 -1
- package/dist/augmentations/defaultAugmentations.js +0 -5
- package/dist/brainy.d.ts +64 -0
- package/dist/brainy.js +77 -0
- package/dist/conversation/conversationManager.d.ts +176 -0
- package/dist/conversation/conversationManager.js +666 -0
- package/dist/conversation/index.d.ts +8 -0
- package/dist/conversation/index.js +8 -0
- package/dist/conversation/types.d.ts +231 -0
- package/dist/conversation/types.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/mcp/conversationTools.d.ts +88 -0
- package/dist/mcp/conversationTools.js +470 -0
- package/dist/neural/embeddedPatterns.d.ts +1 -1
- package/dist/neural/embeddedPatterns.js +1 -1
- package/dist/neural/naturalLanguageProcessor.js +0 -1
- package/dist/setup.js +0 -1
- package/dist/types/mcpTypes.d.ts +7 -1
- package/dist/unified.js +0 -1
- package/dist/vfs/VirtualFileSystem.d.ts +6 -4
- package/dist/vfs/VirtualFileSystem.js +44 -21
- package/dist/vfs/index.d.ts +0 -5
- package/dist/vfs/index.js +0 -6
- package/dist/vfs/semantic/ProjectionRegistry.d.ts +84 -0
- package/dist/vfs/semantic/ProjectionRegistry.js +118 -0
- package/dist/vfs/semantic/ProjectionStrategy.d.ts +69 -0
- package/dist/vfs/semantic/ProjectionStrategy.js +40 -0
- package/dist/vfs/semantic/SemanticPathParser.d.ts +73 -0
- package/dist/vfs/semantic/SemanticPathParser.js +285 -0
- package/dist/vfs/semantic/SemanticPathResolver.d.ts +99 -0
- package/dist/vfs/semantic/SemanticPathResolver.js +242 -0
- package/dist/vfs/semantic/index.d.ts +17 -0
- package/dist/vfs/semantic/index.js +18 -0
- package/dist/vfs/semantic/projections/AuthorProjection.d.ts +35 -0
- package/dist/vfs/semantic/projections/AuthorProjection.js +74 -0
- package/dist/vfs/semantic/projections/ConceptProjection.d.ts +42 -0
- package/dist/vfs/semantic/projections/ConceptProjection.js +87 -0
- package/dist/vfs/semantic/projections/RelationshipProjection.d.ts +41 -0
- package/dist/vfs/semantic/projections/RelationshipProjection.js +101 -0
- package/dist/vfs/semantic/projections/SimilarityProjection.d.ts +36 -0
- package/dist/vfs/semantic/projections/SimilarityProjection.js +77 -0
- package/dist/vfs/semantic/projections/TagProjection.d.ts +34 -0
- package/dist/vfs/semantic/projections/TagProjection.js +73 -0
- package/dist/vfs/semantic/projections/TemporalProjection.d.ts +35 -0
- package/dist/vfs/semantic/projections/TemporalProjection.js +89 -0
- package/dist/vfs/types.d.ts +1 -8
- package/package.json +1 -1
package/dist/vfs/index.js
CHANGED
|
@@ -15,12 +15,6 @@ export { DirectoryImporter } from './importers/DirectoryImporter.js';
|
|
|
15
15
|
// Streaming
|
|
16
16
|
export { VFSReadStream } from './streams/VFSReadStream.js';
|
|
17
17
|
export { VFSWriteStream } from './streams/VFSWriteStream.js';
|
|
18
|
-
// Knowledge Layer Components (optional via augmentation)
|
|
19
|
-
export { EventRecorder } from './EventRecorder.js';
|
|
20
|
-
export { SemanticVersioning } from './SemanticVersioning.js';
|
|
21
|
-
export { PersistentEntitySystem } from './PersistentEntitySystem.js';
|
|
22
|
-
export { ConceptSystem } from './ConceptSystem.js';
|
|
23
|
-
export { GitBridge } from './GitBridge.js';
|
|
24
18
|
// Convenience alias
|
|
25
19
|
export { VirtualFileSystem as VFS } from './VirtualFileSystem.js';
|
|
26
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection Registry
|
|
3
|
+
*
|
|
4
|
+
* Central registry for all projection strategies
|
|
5
|
+
* Manages strategy lookup and execution
|
|
6
|
+
*/
|
|
7
|
+
import { ProjectionStrategy } from './ProjectionStrategy.js';
|
|
8
|
+
import { Brainy } from '../../brainy.js';
|
|
9
|
+
import { VirtualFileSystem } from '../VirtualFileSystem.js';
|
|
10
|
+
import { VFSEntity } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Registry for projection strategies
|
|
13
|
+
* Allows dynamic registration and lookup of strategies
|
|
14
|
+
*/
|
|
15
|
+
export declare class ProjectionRegistry {
|
|
16
|
+
private strategies;
|
|
17
|
+
/**
|
|
18
|
+
* Register a projection strategy
|
|
19
|
+
* @param strategy - The strategy to register
|
|
20
|
+
* @throws Error if strategy with same name already registered
|
|
21
|
+
*/
|
|
22
|
+
register(strategy: ProjectionStrategy): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get a projection strategy by name
|
|
25
|
+
* @param name - Strategy name
|
|
26
|
+
* @returns The strategy or undefined if not found
|
|
27
|
+
*/
|
|
28
|
+
get(name: string): ProjectionStrategy | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Check if a strategy is registered
|
|
31
|
+
* @param name - Strategy name
|
|
32
|
+
*/
|
|
33
|
+
has(name: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* List all registered strategy names
|
|
36
|
+
*/
|
|
37
|
+
listDimensions(): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Get count of registered strategies
|
|
40
|
+
*/
|
|
41
|
+
count(): number;
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a dimension value to entity IDs
|
|
44
|
+
* Convenience method that looks up strategy and calls resolve()
|
|
45
|
+
*
|
|
46
|
+
* @param dimension - The semantic dimension
|
|
47
|
+
* @param value - The value to resolve
|
|
48
|
+
* @param brain - REAL Brainy instance
|
|
49
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
50
|
+
* @returns Array of entity IDs
|
|
51
|
+
* @throws Error if dimension not registered
|
|
52
|
+
*/
|
|
53
|
+
resolve(dimension: string, value: any, brain: Brainy, vfs: VirtualFileSystem): Promise<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* List entities in a dimension
|
|
56
|
+
* Convenience method for strategies that support listing
|
|
57
|
+
*
|
|
58
|
+
* @param dimension - The semantic dimension
|
|
59
|
+
* @param brain - REAL Brainy instance
|
|
60
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
61
|
+
* @param limit - Max results
|
|
62
|
+
* @returns Array of VFSEntity
|
|
63
|
+
* @throws Error if dimension not registered or doesn't support listing
|
|
64
|
+
*/
|
|
65
|
+
list(dimension: string, brain: Brainy, vfs: VirtualFileSystem, limit?: number): Promise<VFSEntity[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Unregister a strategy
|
|
68
|
+
* Useful for testing or dynamic strategy management
|
|
69
|
+
*
|
|
70
|
+
* @param name - Strategy name to remove
|
|
71
|
+
* @returns true if removed, false if not found
|
|
72
|
+
*/
|
|
73
|
+
unregister(name: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Clear all registered strategies
|
|
76
|
+
* Useful for testing
|
|
77
|
+
*/
|
|
78
|
+
clear(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Get all registered strategies
|
|
81
|
+
* Returns a copy to prevent external modification
|
|
82
|
+
*/
|
|
83
|
+
getAll(): ProjectionStrategy[];
|
|
84
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection Registry
|
|
3
|
+
*
|
|
4
|
+
* Central registry for all projection strategies
|
|
5
|
+
* Manages strategy lookup and execution
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Registry for projection strategies
|
|
9
|
+
* Allows dynamic registration and lookup of strategies
|
|
10
|
+
*/
|
|
11
|
+
export class ProjectionRegistry {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.strategies = new Map();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Register a projection strategy
|
|
17
|
+
* @param strategy - The strategy to register
|
|
18
|
+
* @throws Error if strategy with same name already registered
|
|
19
|
+
*/
|
|
20
|
+
register(strategy) {
|
|
21
|
+
if (this.strategies.has(strategy.name)) {
|
|
22
|
+
throw new Error(`Projection strategy '${strategy.name}' is already registered`);
|
|
23
|
+
}
|
|
24
|
+
this.strategies.set(strategy.name, strategy);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get a projection strategy by name
|
|
28
|
+
* @param name - Strategy name
|
|
29
|
+
* @returns The strategy or undefined if not found
|
|
30
|
+
*/
|
|
31
|
+
get(name) {
|
|
32
|
+
return this.strategies.get(name);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a strategy is registered
|
|
36
|
+
* @param name - Strategy name
|
|
37
|
+
*/
|
|
38
|
+
has(name) {
|
|
39
|
+
return this.strategies.has(name);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* List all registered strategy names
|
|
43
|
+
*/
|
|
44
|
+
listDimensions() {
|
|
45
|
+
return Array.from(this.strategies.keys());
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get count of registered strategies
|
|
49
|
+
*/
|
|
50
|
+
count() {
|
|
51
|
+
return this.strategies.size;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve a dimension value to entity IDs
|
|
55
|
+
* Convenience method that looks up strategy and calls resolve()
|
|
56
|
+
*
|
|
57
|
+
* @param dimension - The semantic dimension
|
|
58
|
+
* @param value - The value to resolve
|
|
59
|
+
* @param brain - REAL Brainy instance
|
|
60
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
61
|
+
* @returns Array of entity IDs
|
|
62
|
+
* @throws Error if dimension not registered
|
|
63
|
+
*/
|
|
64
|
+
async resolve(dimension, value, brain, vfs) {
|
|
65
|
+
const strategy = this.get(dimension);
|
|
66
|
+
if (!strategy) {
|
|
67
|
+
throw new Error(`Unknown projection dimension: ${dimension}. Registered dimensions: ${this.listDimensions().join(', ')}`);
|
|
68
|
+
}
|
|
69
|
+
// Call REAL strategy resolve method
|
|
70
|
+
return await strategy.resolve(brain, vfs, value);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* List entities in a dimension
|
|
74
|
+
* Convenience method for strategies that support listing
|
|
75
|
+
*
|
|
76
|
+
* @param dimension - The semantic dimension
|
|
77
|
+
* @param brain - REAL Brainy instance
|
|
78
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
79
|
+
* @param limit - Max results
|
|
80
|
+
* @returns Array of VFSEntity
|
|
81
|
+
* @throws Error if dimension not registered or doesn't support listing
|
|
82
|
+
*/
|
|
83
|
+
async list(dimension, brain, vfs, limit) {
|
|
84
|
+
const strategy = this.get(dimension);
|
|
85
|
+
if (!strategy) {
|
|
86
|
+
throw new Error(`Unknown projection dimension: ${dimension}`);
|
|
87
|
+
}
|
|
88
|
+
if (!strategy.list) {
|
|
89
|
+
throw new Error(`Projection '${dimension}' does not support listing`);
|
|
90
|
+
}
|
|
91
|
+
return await strategy.list(brain, vfs, limit);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Unregister a strategy
|
|
95
|
+
* Useful for testing or dynamic strategy management
|
|
96
|
+
*
|
|
97
|
+
* @param name - Strategy name to remove
|
|
98
|
+
* @returns true if removed, false if not found
|
|
99
|
+
*/
|
|
100
|
+
unregister(name) {
|
|
101
|
+
return this.strategies.delete(name);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Clear all registered strategies
|
|
105
|
+
* Useful for testing
|
|
106
|
+
*/
|
|
107
|
+
clear() {
|
|
108
|
+
this.strategies.clear();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get all registered strategies
|
|
112
|
+
* Returns a copy to prevent external modification
|
|
113
|
+
*/
|
|
114
|
+
getAll() {
|
|
115
|
+
return Array.from(this.strategies.values());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=ProjectionRegistry.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection Strategy Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines how to map semantic path dimensions to Brainy queries
|
|
5
|
+
* Each strategy uses EXISTING Brainy indexes and methods
|
|
6
|
+
*/
|
|
7
|
+
import { Brainy } from '../../brainy.js';
|
|
8
|
+
import { VirtualFileSystem } from '../VirtualFileSystem.js';
|
|
9
|
+
import { FindParams } from '../../types/brainy.types.js';
|
|
10
|
+
import { VFSEntity } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Strategy for projecting semantic paths into entity queries
|
|
13
|
+
* All implementations MUST use real Brainy methods (no stubs!)
|
|
14
|
+
*/
|
|
15
|
+
export interface ProjectionStrategy {
|
|
16
|
+
/**
|
|
17
|
+
* Strategy name (used for registration)
|
|
18
|
+
*/
|
|
19
|
+
readonly name: string;
|
|
20
|
+
/**
|
|
21
|
+
* Convert semantic value to Brainy FindParams
|
|
22
|
+
* Uses EXISTING FindParams type from brainy.types.ts
|
|
23
|
+
*/
|
|
24
|
+
toQuery(value: any, subpath?: string): FindParams;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve semantic value to entity IDs
|
|
27
|
+
* Uses REAL Brainy.find() method
|
|
28
|
+
*
|
|
29
|
+
* @param brain - REAL Brainy instance
|
|
30
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
31
|
+
* @param value - The semantic value to resolve
|
|
32
|
+
* @returns Array of entity IDs that match
|
|
33
|
+
*/
|
|
34
|
+
resolve(brain: Brainy, vfs: VirtualFileSystem, value: any): Promise<string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* List all entities in this dimension
|
|
37
|
+
* Optional - not all strategies need to implement
|
|
38
|
+
*
|
|
39
|
+
* @param brain - REAL Brainy instance
|
|
40
|
+
* @param vfs - REAL VirtualFileSystem instance
|
|
41
|
+
* @param limit - Max results to return
|
|
42
|
+
*/
|
|
43
|
+
list?(brain: Brainy, vfs: VirtualFileSystem, limit?: number): Promise<VFSEntity[]>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Base class for projection strategies with common utilities
|
|
47
|
+
*/
|
|
48
|
+
export declare abstract class BaseProjectionStrategy implements ProjectionStrategy {
|
|
49
|
+
abstract readonly name: string;
|
|
50
|
+
abstract toQuery(value: any, subpath?: string): FindParams;
|
|
51
|
+
abstract resolve(brain: Brainy, vfs: VirtualFileSystem, value: any): Promise<string[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Convert Brainy Results to entity IDs
|
|
54
|
+
* Helper method for subclasses
|
|
55
|
+
*/
|
|
56
|
+
protected extractIds(results: Array<{
|
|
57
|
+
id: string;
|
|
58
|
+
}>): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Verify that an entity is a file (not directory)
|
|
61
|
+
* Uses REAL Brainy.get() method
|
|
62
|
+
*/
|
|
63
|
+
protected isFile(brain: Brainy, entityId: string): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Filter entity IDs to only include files
|
|
66
|
+
* Uses REAL Brainy.get() for each entity
|
|
67
|
+
*/
|
|
68
|
+
protected filterFiles(brain: Brainy, entityIds: string[]): Promise<string[]>;
|
|
69
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection Strategy Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines how to map semantic path dimensions to Brainy queries
|
|
5
|
+
* Each strategy uses EXISTING Brainy indexes and methods
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Base class for projection strategies with common utilities
|
|
9
|
+
*/
|
|
10
|
+
export class BaseProjectionStrategy {
|
|
11
|
+
/**
|
|
12
|
+
* Convert Brainy Results to entity IDs
|
|
13
|
+
* Helper method for subclasses
|
|
14
|
+
*/
|
|
15
|
+
extractIds(results) {
|
|
16
|
+
return results.map(r => r.id);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Verify that an entity is a file (not directory)
|
|
20
|
+
* Uses REAL Brainy.get() method
|
|
21
|
+
*/
|
|
22
|
+
async isFile(brain, entityId) {
|
|
23
|
+
const entity = await brain.get(entityId);
|
|
24
|
+
return entity?.metadata?.vfsType === 'file';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Filter entity IDs to only include files
|
|
28
|
+
* Uses REAL Brainy.get() for each entity
|
|
29
|
+
*/
|
|
30
|
+
async filterFiles(brain, entityIds) {
|
|
31
|
+
const files = [];
|
|
32
|
+
for (const id of entityIds) {
|
|
33
|
+
if (await this.isFile(brain, id)) {
|
|
34
|
+
files.push(id);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return files;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=ProjectionStrategy.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Path Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses semantic filesystem paths into structured queries
|
|
5
|
+
* PURE LOGIC - No external dependencies, no async operations
|
|
6
|
+
*
|
|
7
|
+
* Supported path formats:
|
|
8
|
+
* - Traditional: /src/auth.ts
|
|
9
|
+
* - By Concept: /by-concept/authentication/login.ts
|
|
10
|
+
* - By Author: /by-author/alice/file.ts
|
|
11
|
+
* - By Time: /as-of/2024-03-15/file.ts
|
|
12
|
+
* - By Relationship: /related-to/src/auth.ts/depth-2
|
|
13
|
+
* - By Similarity: /similar-to/src/auth.ts/threshold-0.8
|
|
14
|
+
* - By Tag: /by-tag/security/file.ts
|
|
15
|
+
*/
|
|
16
|
+
export type SemanticDimension = 'traditional' | 'concept' | 'author' | 'time' | 'relationship' | 'similar' | 'tag';
|
|
17
|
+
export interface ParsedSemanticPath {
|
|
18
|
+
dimension: SemanticDimension;
|
|
19
|
+
value: string | Date | RelationshipValue | SimilarityValue;
|
|
20
|
+
subpath?: string;
|
|
21
|
+
filters?: Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
export interface RelationshipValue {
|
|
24
|
+
targetPath: string;
|
|
25
|
+
depth?: number;
|
|
26
|
+
relationshipTypes?: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface SimilarityValue {
|
|
29
|
+
targetPath: string;
|
|
30
|
+
threshold?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Semantic Path Parser
|
|
34
|
+
* Parses various semantic path formats into structured data
|
|
35
|
+
*/
|
|
36
|
+
export declare class SemanticPathParser {
|
|
37
|
+
private static readonly PATTERNS;
|
|
38
|
+
/**
|
|
39
|
+
* Parse a path into semantic components
|
|
40
|
+
* PURE FUNCTION - no external calls, no async
|
|
41
|
+
*/
|
|
42
|
+
parse(path: string): ParsedSemanticPath;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a path is semantic (non-traditional)
|
|
45
|
+
*/
|
|
46
|
+
isSemanticPath(path: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Get the dimension type from a path
|
|
49
|
+
*/
|
|
50
|
+
getDimension(path: string): SemanticDimension;
|
|
51
|
+
/**
|
|
52
|
+
* Normalize a path - remove trailing slashes, collapse multiple slashes
|
|
53
|
+
* PURE FUNCTION
|
|
54
|
+
*/
|
|
55
|
+
private normalizePath;
|
|
56
|
+
/**
|
|
57
|
+
* Parse date string (YYYY-MM-DD) into Date object
|
|
58
|
+
* PURE FUNCTION
|
|
59
|
+
*/
|
|
60
|
+
private parseDate;
|
|
61
|
+
/**
|
|
62
|
+
* Validate parsed path structure
|
|
63
|
+
*/
|
|
64
|
+
validate(parsed: ParsedSemanticPath): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Parse relationship paths: /related-to/<path>/depth-N/types-X,Y/<subpath>
|
|
67
|
+
*/
|
|
68
|
+
private parseRelationshipPath;
|
|
69
|
+
/**
|
|
70
|
+
* Parse similarity paths: /similar-to/<path>/threshold-N/<subpath>
|
|
71
|
+
*/
|
|
72
|
+
private parseSimilarityPath;
|
|
73
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Path Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses semantic filesystem paths into structured queries
|
|
5
|
+
* PURE LOGIC - No external dependencies, no async operations
|
|
6
|
+
*
|
|
7
|
+
* Supported path formats:
|
|
8
|
+
* - Traditional: /src/auth.ts
|
|
9
|
+
* - By Concept: /by-concept/authentication/login.ts
|
|
10
|
+
* - By Author: /by-author/alice/file.ts
|
|
11
|
+
* - By Time: /as-of/2024-03-15/file.ts
|
|
12
|
+
* - By Relationship: /related-to/src/auth.ts/depth-2
|
|
13
|
+
* - By Similarity: /similar-to/src/auth.ts/threshold-0.8
|
|
14
|
+
* - By Tag: /by-tag/security/file.ts
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Semantic Path Parser
|
|
18
|
+
* Parses various semantic path formats into structured data
|
|
19
|
+
*/
|
|
20
|
+
export class SemanticPathParser {
|
|
21
|
+
/**
|
|
22
|
+
* Parse a path into semantic components
|
|
23
|
+
* PURE FUNCTION - no external calls, no async
|
|
24
|
+
*/
|
|
25
|
+
parse(path) {
|
|
26
|
+
if (!path || typeof path !== 'string') {
|
|
27
|
+
throw new Error('Path must be a non-empty string');
|
|
28
|
+
}
|
|
29
|
+
// Normalize path
|
|
30
|
+
const normalized = this.normalizePath(path);
|
|
31
|
+
// Try concept dimension
|
|
32
|
+
const conceptMatch = normalized.match(SemanticPathParser.PATTERNS.concept);
|
|
33
|
+
if (conceptMatch) {
|
|
34
|
+
return {
|
|
35
|
+
dimension: 'concept',
|
|
36
|
+
value: conceptMatch[1],
|
|
37
|
+
subpath: conceptMatch[2]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Try author dimension
|
|
41
|
+
const authorMatch = normalized.match(SemanticPathParser.PATTERNS.author);
|
|
42
|
+
if (authorMatch) {
|
|
43
|
+
return {
|
|
44
|
+
dimension: 'author',
|
|
45
|
+
value: authorMatch[1],
|
|
46
|
+
subpath: authorMatch[2]
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Try time dimension
|
|
50
|
+
const timeMatch = normalized.match(SemanticPathParser.PATTERNS.time);
|
|
51
|
+
if (timeMatch) {
|
|
52
|
+
const dateStr = timeMatch[1];
|
|
53
|
+
const date = this.parseDate(dateStr);
|
|
54
|
+
return {
|
|
55
|
+
dimension: 'time',
|
|
56
|
+
value: date,
|
|
57
|
+
subpath: timeMatch[2]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Try relationship dimension
|
|
61
|
+
if (normalized.startsWith('/related-to/')) {
|
|
62
|
+
return this.parseRelationshipPath(normalized);
|
|
63
|
+
}
|
|
64
|
+
// Try similarity dimension
|
|
65
|
+
if (normalized.startsWith('/similar-to/')) {
|
|
66
|
+
return this.parseSimilarityPath(normalized);
|
|
67
|
+
}
|
|
68
|
+
// Try tag dimension
|
|
69
|
+
const tagMatch = normalized.match(SemanticPathParser.PATTERNS.tag);
|
|
70
|
+
if (tagMatch) {
|
|
71
|
+
return {
|
|
72
|
+
dimension: 'tag',
|
|
73
|
+
value: tagMatch[1],
|
|
74
|
+
subpath: tagMatch[2]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Default to traditional path
|
|
78
|
+
return {
|
|
79
|
+
dimension: 'traditional',
|
|
80
|
+
value: normalized
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check if a path is semantic (non-traditional)
|
|
85
|
+
*/
|
|
86
|
+
isSemanticPath(path) {
|
|
87
|
+
if (!path || typeof path !== 'string') {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
const normalized = this.normalizePath(path);
|
|
91
|
+
// Check if matches any semantic pattern
|
|
92
|
+
return (normalized.startsWith('/by-concept/') ||
|
|
93
|
+
normalized.startsWith('/by-author/') ||
|
|
94
|
+
normalized.startsWith('/as-of/') ||
|
|
95
|
+
normalized.startsWith('/related-to/') ||
|
|
96
|
+
normalized.startsWith('/similar-to/') ||
|
|
97
|
+
normalized.startsWith('/by-tag/'));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get the dimension type from a path
|
|
101
|
+
*/
|
|
102
|
+
getDimension(path) {
|
|
103
|
+
return this.parse(path).dimension;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Normalize a path - remove trailing slashes, collapse multiple slashes
|
|
107
|
+
* PURE FUNCTION
|
|
108
|
+
*/
|
|
109
|
+
normalizePath(path) {
|
|
110
|
+
// Remove trailing slash (except for root)
|
|
111
|
+
let normalized = path.replace(/\/+$/, '');
|
|
112
|
+
// Collapse multiple slashes
|
|
113
|
+
normalized = normalized.replace(/\/+/g, '/');
|
|
114
|
+
// Ensure starts with /
|
|
115
|
+
if (!normalized.startsWith('/')) {
|
|
116
|
+
normalized = '/' + normalized;
|
|
117
|
+
}
|
|
118
|
+
// Special case: empty path becomes /
|
|
119
|
+
if (normalized === '') {
|
|
120
|
+
normalized = '/';
|
|
121
|
+
}
|
|
122
|
+
return normalized;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse date string (YYYY-MM-DD) into Date object
|
|
126
|
+
* PURE FUNCTION
|
|
127
|
+
*/
|
|
128
|
+
parseDate(dateStr) {
|
|
129
|
+
const parts = dateStr.split('-');
|
|
130
|
+
if (parts.length !== 3) {
|
|
131
|
+
throw new Error(`Invalid date format: ${dateStr}. Expected YYYY-MM-DD`);
|
|
132
|
+
}
|
|
133
|
+
const year = parseInt(parts[0], 10);
|
|
134
|
+
const month = parseInt(parts[1], 10) - 1; // Months are 0-indexed in JS
|
|
135
|
+
const day = parseInt(parts[2], 10);
|
|
136
|
+
if (isNaN(year) || isNaN(month) || isNaN(day)) {
|
|
137
|
+
throw new Error(`Invalid date format: ${dateStr}. Expected YYYY-MM-DD`);
|
|
138
|
+
}
|
|
139
|
+
if (year < 1900 || year > 2100) {
|
|
140
|
+
throw new Error(`Invalid year: ${year}. Expected 1900-2100`);
|
|
141
|
+
}
|
|
142
|
+
if (month < 0 || month > 11) {
|
|
143
|
+
throw new Error(`Invalid month: ${month + 1}. Expected 1-12`);
|
|
144
|
+
}
|
|
145
|
+
if (day < 1 || day > 31) {
|
|
146
|
+
throw new Error(`Invalid day: ${day}. Expected 1-31`);
|
|
147
|
+
}
|
|
148
|
+
return new Date(year, month, day);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validate parsed path structure
|
|
152
|
+
*/
|
|
153
|
+
validate(parsed) {
|
|
154
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
if (!parsed.dimension) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
if (parsed.value === undefined || parsed.value === null) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
// Dimension-specific validation
|
|
164
|
+
switch (parsed.dimension) {
|
|
165
|
+
case 'time':
|
|
166
|
+
return parsed.value instanceof Date && !isNaN(parsed.value.getTime());
|
|
167
|
+
case 'relationship':
|
|
168
|
+
const relValue = parsed.value;
|
|
169
|
+
return typeof relValue.targetPath === 'string' && relValue.targetPath.length > 0;
|
|
170
|
+
case 'similar':
|
|
171
|
+
const simValue = parsed.value;
|
|
172
|
+
return typeof simValue.targetPath === 'string' && simValue.targetPath.length > 0;
|
|
173
|
+
default:
|
|
174
|
+
return typeof parsed.value === 'string' && parsed.value.length > 0;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Parse relationship paths: /related-to/<path>/depth-N/types-X,Y/<subpath>
|
|
179
|
+
*/
|
|
180
|
+
parseRelationshipPath(path) {
|
|
181
|
+
// Remove /related-to/ prefix
|
|
182
|
+
const withoutPrefix = path.substring('/related-to/'.length);
|
|
183
|
+
// Split into segments
|
|
184
|
+
const segments = withoutPrefix.split('/');
|
|
185
|
+
let targetPath = '';
|
|
186
|
+
let depth;
|
|
187
|
+
let types;
|
|
188
|
+
let subpath;
|
|
189
|
+
let i = 0;
|
|
190
|
+
// Collect path segments until we hit depth-, types-, or end
|
|
191
|
+
while (i < segments.length) {
|
|
192
|
+
const segment = segments[i];
|
|
193
|
+
if (segment.startsWith('depth-')) {
|
|
194
|
+
depth = parseInt(segment.substring('depth-'.length), 10);
|
|
195
|
+
i++;
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (segment.startsWith('types-')) {
|
|
199
|
+
types = segment.substring('types-'.length).split(',');
|
|
200
|
+
i++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
// If we've already collected the target path and found depth/types,
|
|
204
|
+
// rest is subpath
|
|
205
|
+
if (targetPath && (depth !== undefined || types !== undefined)) {
|
|
206
|
+
subpath = segments.slice(i).join('/');
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
// Add to target path
|
|
210
|
+
if (targetPath) {
|
|
211
|
+
targetPath += '/' + segment;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
targetPath = segment;
|
|
215
|
+
}
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
const value = {
|
|
219
|
+
targetPath,
|
|
220
|
+
depth,
|
|
221
|
+
relationshipTypes: types
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
dimension: 'relationship',
|
|
225
|
+
value,
|
|
226
|
+
subpath
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Parse similarity paths: /similar-to/<path>/threshold-N/<subpath>
|
|
231
|
+
*/
|
|
232
|
+
parseSimilarityPath(path) {
|
|
233
|
+
// Remove /similar-to/ prefix
|
|
234
|
+
const withoutPrefix = path.substring('/similar-to/'.length);
|
|
235
|
+
// Split into segments
|
|
236
|
+
const segments = withoutPrefix.split('/');
|
|
237
|
+
let targetPath = '';
|
|
238
|
+
let threshold;
|
|
239
|
+
let subpath;
|
|
240
|
+
let i = 0;
|
|
241
|
+
// Collect path segments until we hit threshold- or end
|
|
242
|
+
while (i < segments.length) {
|
|
243
|
+
const segment = segments[i];
|
|
244
|
+
if (segment.startsWith('threshold-')) {
|
|
245
|
+
threshold = parseFloat(segment.substring('threshold-'.length));
|
|
246
|
+
i++;
|
|
247
|
+
// Rest is subpath
|
|
248
|
+
if (i < segments.length) {
|
|
249
|
+
subpath = segments.slice(i).join('/');
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
// Add to target path
|
|
254
|
+
if (targetPath) {
|
|
255
|
+
targetPath += '/' + segment;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
targetPath = segment;
|
|
259
|
+
}
|
|
260
|
+
i++;
|
|
261
|
+
}
|
|
262
|
+
const value = {
|
|
263
|
+
targetPath,
|
|
264
|
+
threshold
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
dimension: 'similar',
|
|
268
|
+
value,
|
|
269
|
+
subpath
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Regex patterns for each dimension
|
|
274
|
+
SemanticPathParser.PATTERNS = {
|
|
275
|
+
concept: /^\/by-concept\/([^\/]+)(?:\/(.+))?$/,
|
|
276
|
+
author: /^\/by-author\/([^\/]+)(?:\/(.+))?$/,
|
|
277
|
+
time: /^\/as-of\/(\d{4}-\d{2}-\d{2})(?:\/(.+))?$/,
|
|
278
|
+
// Relationship: /related-to/<path>/depth-N/types-X,Y/<subpath>
|
|
279
|
+
// Must handle paths with slashes, so capture everything before /depth- or /types-
|
|
280
|
+
relationship: /^\/related-to\/(.+?)(?:\/depth-(\d+)|\/types-([^\/]+)|\/(.+))*$/,
|
|
281
|
+
// Similarity: /similar-to/<path>/threshold-N/<subpath>
|
|
282
|
+
similar: /^\/similar-to\/(.+?)(?:\/threshold-([\d.]+)|\/(.+))*$/,
|
|
283
|
+
tag: /^\/by-tag\/([^\/]+)(?:\/(.+))?$/
|
|
284
|
+
};
|
|
285
|
+
//# sourceMappingURL=SemanticPathParser.js.map
|