@canon-protocol/sdk 8.3.0 → 8.5.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/README.md +130 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.js +28 -0
- package/dist/graph/GraphBuilder.d.ts +97 -0
- package/dist/graph/GraphBuilder.js +279 -0
- package/dist/graph/index.d.ts +3 -0
- package/dist/graph/index.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +6 -2
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @canon-protocol/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for [Canon Protocol](https://canon-protocol.org) — parse, validate, resolve, and visualize semantic ontologies.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @canon-protocol/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
### Parse a Canon document
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { CanonParser } from '@canon-protocol/sdk';
|
|
17
|
+
|
|
18
|
+
const parser = new CanonParser();
|
|
19
|
+
const doc = parser.parse(yamlContent);
|
|
20
|
+
|
|
21
|
+
// doc.metadata — package info, publisher, version, imports
|
|
22
|
+
// doc.body — all entities (classes, properties, instances) as key-value pairs
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Validate
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { CanonParser, CanonObjectValidator, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
29
|
+
|
|
30
|
+
const parser = new CanonParser();
|
|
31
|
+
const repo = new InMemoryCanonDocumentRepository(parser);
|
|
32
|
+
|
|
33
|
+
// Load one or more documents
|
|
34
|
+
const doc = parser.parse(yamlContent);
|
|
35
|
+
await repo.saveDocumentAsync(doc, 'my-ontology');
|
|
36
|
+
|
|
37
|
+
// Validate
|
|
38
|
+
const validator = new CanonObjectValidator(parser);
|
|
39
|
+
const result = await validator.validateAsync(doc, repo);
|
|
40
|
+
|
|
41
|
+
for (const error of result.errors) {
|
|
42
|
+
console.log(`${error.severity}: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Extract a graph for visualization
|
|
47
|
+
|
|
48
|
+
Returns nodes and edges ready for D3, Cytoscape, React Flow, vis.js, or any graph library:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { GraphBuilder, CanonParser, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
52
|
+
|
|
53
|
+
const parser = new CanonParser();
|
|
54
|
+
const repo = new InMemoryCanonDocumentRepository(parser);
|
|
55
|
+
const doc = parser.parse(yamlContent);
|
|
56
|
+
await repo.saveDocumentAsync(doc, 'contacts');
|
|
57
|
+
|
|
58
|
+
const graph = await GraphBuilder.buildFromRepository(repo);
|
|
59
|
+
|
|
60
|
+
// graph.nodes — each node has: id, label, type, namespace, properties
|
|
61
|
+
// type is one of: Class, ObjectProperty, DatatypeProperty, Instance, Datatype, Unknown
|
|
62
|
+
//
|
|
63
|
+
// graph.edges — each edge has: source, target, type, label
|
|
64
|
+
// type is one of: instanceOf, subClassOf, domain, range, objectRelationship, subPropertyOf
|
|
65
|
+
|
|
66
|
+
// Quick single-document graph (no repository needed):
|
|
67
|
+
const simpleGraph = GraphBuilder.buildFromDocument(doc);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Resolve entity references across imports
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { ResourceResolver, CanonParser, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
74
|
+
|
|
75
|
+
const parser = new CanonParser();
|
|
76
|
+
const repo = new InMemoryCanonDocumentRepository(parser);
|
|
77
|
+
// ... load documents into repo ...
|
|
78
|
+
|
|
79
|
+
const resolver = new ResourceResolver(repo);
|
|
80
|
+
const entity = await resolver.resolveEntityAsync('Person', doc);
|
|
81
|
+
// entity.uri — fully qualified Canon URI
|
|
82
|
+
// entity.entity — the raw entity definition
|
|
83
|
+
// entity.isImported — whether it came from an imported package
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Browser usage
|
|
87
|
+
|
|
88
|
+
For browser applications (no Node.js APIs), import from the browser entry point:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import {
|
|
92
|
+
CanonParser,
|
|
93
|
+
GraphBuilder,
|
|
94
|
+
InMemoryCanonDocumentRepository,
|
|
95
|
+
HttpCanonDocumentRepository,
|
|
96
|
+
CanonObjectValidator,
|
|
97
|
+
} from '@canon-protocol/sdk/browser';
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This includes parsing, validation, graph building, resolution, and HTTP/in-memory repositories. It excludes filesystem access, OS credential stores, and other Node.js-specific modules.
|
|
101
|
+
|
|
102
|
+
Works with Vite, webpack, esbuild, Rollup, or any browser bundler.
|
|
103
|
+
|
|
104
|
+
## Document repositories
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import {
|
|
108
|
+
InMemoryCanonDocumentRepository, // In-memory — for testing, browser, or single-document work
|
|
109
|
+
FileSystemCanonDocumentRepository, // Filesystem — scan a directory of .can.yml files (Node.js only)
|
|
110
|
+
HttpCanonDocumentRepository, // HTTP — fetch packages from publisher domains
|
|
111
|
+
} from '@canon-protocol/sdk';
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Key modules
|
|
115
|
+
|
|
116
|
+
| Module | Description | Browser-safe |
|
|
117
|
+
|--------|-------------|:---:|
|
|
118
|
+
| `CanonParser` | Parse `.can.yml` YAML into `CanonDocument` objects | Yes |
|
|
119
|
+
| `CanonObjectValidator` | Validate documents against ontology rules | Yes |
|
|
120
|
+
| `GraphBuilder` | Extract nodes and edges for graph visualization | Yes |
|
|
121
|
+
| `ResourceResolver` | Resolve entity references across imports | Yes |
|
|
122
|
+
| `InMemoryCanonDocumentRepository` | In-memory document store | Yes |
|
|
123
|
+
| `HttpCanonDocumentRepository` | Fetch packages from publisher HTTP endpoints | Yes |
|
|
124
|
+
| `PublisherIndex` | Discover and resolve package versions from publishers | Yes |
|
|
125
|
+
| `FileSystemCanonDocumentRepository` | Read/write `.can.yml` files on disk | No |
|
|
126
|
+
| `CredentialStore` | OS-native credential storage (Keychain, CredMan, etc.) | No |
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
Apache-2.0
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe entry point for the Canon Protocol SDK.
|
|
3
|
+
*
|
|
4
|
+
* Import from '@canon-protocol/sdk/browser' in browser applications.
|
|
5
|
+
* This entry point excludes Node.js-specific modules (filesystem repos,
|
|
6
|
+
* OS credential stores, child_process, node:crypto).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { CanonParser, GraphBuilder, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk/browser';
|
|
10
|
+
*/
|
|
11
|
+
export { CanonParser, CanonObjectParser, PropertyMetadata } from './parsing/index.js';
|
|
12
|
+
export type { ICanonObjectParser } from './parsing/index.js';
|
|
13
|
+
export { GraphBuilder, NodeType, EdgeType } from './graph/index.js';
|
|
14
|
+
export type { GraphNode, GraphEdge, GraphData } from './graph/index.js';
|
|
15
|
+
export { OntologyValidationResult, OntologyValidationError, ValidationSeverity, ValidationContext, CanonObjectValidator, NamespacePrefixRule, ResourceNamingRule, PropertyTypeSpecificityRule, SubjectCanonTypeRequiredRule, EmbeddedCanonNoExplicitTypeRule, ImportExistenceRule, UnresolvedReferenceRule, TypeAmbiguityRule, ClassHierarchyCycleRule, PropertyHierarchyCycleRule, PropertyRangeRequiredRule, SubClassOfReferenceRule, SubPropertyOfReferenceRule, NamespaceImportCycleRule, InstancePropertyReferenceRule, DefinitionPropertyReferenceRule, XsdImportRule, AmbiguousReferenceRule, PropertyRangeReferenceRule, ObjectPropertyImportRule, ObjectPropertyValueValidationRule, PropertyDomainRule, PropertyValueTypeRule, ClassDefinitionRule } from './validation/index.js';
|
|
16
|
+
export type { ICanonObjectValidator, IDocumentValidationRule, IRepositoryValidationRule } from './validation/index.js';
|
|
17
|
+
export { Canon, DefinedCanon, SubjectCanon, EmbeddedCanon, ReferenceCanon } from './canons/index.js';
|
|
18
|
+
export type { IStatement } from './statements/index.js';
|
|
19
|
+
export { Statement, ScalarStatement, StringStatement, NumberStatement, BooleanStatement, ReferenceStatement, EmbeddedStatement, ListStatement } from './statements/index.js';
|
|
20
|
+
export { ResourceResolver, CanonUri, CanonUriBuilder, TypeResolver } from './resolution/index.js';
|
|
21
|
+
export type { ResourceResolutionResult, ILogger } from './resolution/index.js';
|
|
22
|
+
export { InMemoryCanonDocumentRepository } from './repositories/InMemoryCanonDocumentRepository.js';
|
|
23
|
+
export { HttpCanonDocumentRepository } from './repositories/HttpCanonDocumentRepository.js';
|
|
24
|
+
export { PublisherIndex } from './repositories/PublisherIndex.js';
|
|
25
|
+
export { PublisherConfigResolver } from './repositories/PublisherConfig.js';
|
|
26
|
+
export type { PublisherConfig } from './repositories/PublisherConfig.js';
|
|
27
|
+
export { CtlCanonUri, CtlParser, ResourceTypeClassifier, CtlValidator, CtlGraphResolver, CtlMarkdownRenderer, CtlValidationErrorType, CtlValidationSeverity, CtlCanonUriType, PathSegmentType, ResolvedResourceType } from './ctl/index.js';
|
|
28
|
+
export type { CtlDocument, CanonReference, CtlValidationResult, CtlValidationError, ResolvedResourceGraph, ResolvedResource, ResolvedProperty, UnresolvedResource } from './ctl/index.js';
|
|
29
|
+
export type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
|
|
30
|
+
export type { ICanonParser } from '@canon-protocol/types/document/parsing';
|
|
31
|
+
export type { CanonDocument, CanonMetadata, Namespace, Import, Version, DocumentReference, ParseResult, ParseError } from '@canon-protocol/types/document/models/types';
|
|
32
|
+
export type { VersionOperator } from '@canon-protocol/types/document/models/enums';
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe entry point for the Canon Protocol SDK.
|
|
3
|
+
*
|
|
4
|
+
* Import from '@canon-protocol/sdk/browser' in browser applications.
|
|
5
|
+
* This entry point excludes Node.js-specific modules (filesystem repos,
|
|
6
|
+
* OS credential stores, child_process, node:crypto).
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* import { CanonParser, GraphBuilder, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk/browser';
|
|
10
|
+
*/
|
|
11
|
+
// Parsing
|
|
12
|
+
export { CanonParser, CanonObjectParser, PropertyMetadata } from './parsing/index.js';
|
|
13
|
+
// Graph
|
|
14
|
+
export { GraphBuilder, NodeType, EdgeType } from './graph/index.js';
|
|
15
|
+
// Validation
|
|
16
|
+
export { OntologyValidationResult, OntologyValidationError, ValidationSeverity, ValidationContext, CanonObjectValidator, NamespacePrefixRule, ResourceNamingRule, PropertyTypeSpecificityRule, SubjectCanonTypeRequiredRule, EmbeddedCanonNoExplicitTypeRule, ImportExistenceRule, UnresolvedReferenceRule, TypeAmbiguityRule, ClassHierarchyCycleRule, PropertyHierarchyCycleRule, PropertyRangeRequiredRule, SubClassOfReferenceRule, SubPropertyOfReferenceRule, NamespaceImportCycleRule, InstancePropertyReferenceRule, DefinitionPropertyReferenceRule, XsdImportRule, AmbiguousReferenceRule, PropertyRangeReferenceRule, ObjectPropertyImportRule, ObjectPropertyValueValidationRule, PropertyDomainRule, PropertyValueTypeRule, ClassDefinitionRule } from './validation/index.js';
|
|
17
|
+
// Canons
|
|
18
|
+
export { Canon, DefinedCanon, SubjectCanon, EmbeddedCanon, ReferenceCanon } from './canons/index.js';
|
|
19
|
+
export { Statement, ScalarStatement, StringStatement, NumberStatement, BooleanStatement, ReferenceStatement, EmbeddedStatement, ListStatement } from './statements/index.js';
|
|
20
|
+
// Resolution
|
|
21
|
+
export { ResourceResolver, CanonUri, CanonUriBuilder, TypeResolver } from './resolution/index.js';
|
|
22
|
+
// Repositories (browser-safe only)
|
|
23
|
+
export { InMemoryCanonDocumentRepository } from './repositories/InMemoryCanonDocumentRepository.js';
|
|
24
|
+
export { HttpCanonDocumentRepository } from './repositories/HttpCanonDocumentRepository.js';
|
|
25
|
+
export { PublisherIndex } from './repositories/PublisherIndex.js';
|
|
26
|
+
export { PublisherConfigResolver } from './repositories/PublisherConfig.js';
|
|
27
|
+
// CTL
|
|
28
|
+
export { CtlCanonUri, CtlParser, ResourceTypeClassifier, CtlValidator, CtlGraphResolver, CtlMarkdownRenderer, CtlValidationErrorType, CtlValidationSeverity, CtlCanonUriType, PathSegmentType, ResolvedResourceType } from './ctl/index.js';
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { CanonDocument } from '@canon-protocol/types/document/models/types';
|
|
2
|
+
import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
|
|
3
|
+
/**
|
|
4
|
+
* Node types in a Canon graph.
|
|
5
|
+
*/
|
|
6
|
+
export declare enum NodeType {
|
|
7
|
+
Class = "Class",
|
|
8
|
+
DatatypeProperty = "DatatypeProperty",
|
|
9
|
+
ObjectProperty = "ObjectProperty",
|
|
10
|
+
AnnotationProperty = "AnnotationProperty",
|
|
11
|
+
Instance = "Instance",
|
|
12
|
+
Datatype = "Datatype",
|
|
13
|
+
Unknown = "Unknown"
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Edge types in a Canon graph.
|
|
17
|
+
*/
|
|
18
|
+
export declare enum EdgeType {
|
|
19
|
+
/** Instance → Class (type) */
|
|
20
|
+
InstanceOf = "instanceOf",
|
|
21
|
+
/** Class → Class (subClassOf) */
|
|
22
|
+
SubClassOf = "subClassOf",
|
|
23
|
+
/** Property → Class (domain) */
|
|
24
|
+
Domain = "domain",
|
|
25
|
+
/** Property → Class or Datatype (range) */
|
|
26
|
+
Range = "range",
|
|
27
|
+
/** Class → Class via ObjectProperty */
|
|
28
|
+
ObjectRelationship = "objectRelationship",
|
|
29
|
+
/** Property → Property (subPropertyOf) */
|
|
30
|
+
SubPropertyOf = "subPropertyOf",
|
|
31
|
+
/** Instance → value via property */
|
|
32
|
+
PropertyValue = "propertyValue"
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A node in the Canon graph.
|
|
36
|
+
*/
|
|
37
|
+
export interface GraphNode {
|
|
38
|
+
/** Unique identifier: "publisher/package@version/entityName" or just "entityName" for local */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Display name */
|
|
41
|
+
label: string;
|
|
42
|
+
/** Semantic type */
|
|
43
|
+
type: NodeType;
|
|
44
|
+
/** Namespace this entity belongs to (publisher/package@version) */
|
|
45
|
+
namespace: string;
|
|
46
|
+
/** All properties as key-value pairs for tooltips/detail panels */
|
|
47
|
+
properties: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* An edge in the Canon graph.
|
|
51
|
+
*/
|
|
52
|
+
export interface GraphEdge {
|
|
53
|
+
/** Source node id */
|
|
54
|
+
source: string;
|
|
55
|
+
/** Target node id */
|
|
56
|
+
target: string;
|
|
57
|
+
/** Relationship type */
|
|
58
|
+
type: EdgeType;
|
|
59
|
+
/** Display label (e.g., property name) */
|
|
60
|
+
label: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Complete graph data ready for visualization libraries.
|
|
64
|
+
*/
|
|
65
|
+
export interface GraphData {
|
|
66
|
+
nodes: GraphNode[];
|
|
67
|
+
edges: GraphEdge[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Builds a visualization-ready graph from Canon documents.
|
|
71
|
+
*
|
|
72
|
+
* Usage:
|
|
73
|
+
* ```typescript
|
|
74
|
+
* import { GraphBuilder, CanonParser, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
75
|
+
*
|
|
76
|
+
* const parser = new CanonParser();
|
|
77
|
+
* const repo = new InMemoryCanonDocumentRepository(parser);
|
|
78
|
+
* const doc = parser.parse(yamlContent);
|
|
79
|
+
* await repo.saveDocumentAsync(doc, 'my-doc');
|
|
80
|
+
*
|
|
81
|
+
* const graph = await GraphBuilder.buildFromRepository(repo);
|
|
82
|
+
* // graph.nodes = [{ id, label, type, namespace, properties }, ...]
|
|
83
|
+
* // graph.edges = [{ source, target, type, label }, ...]
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare class GraphBuilder {
|
|
87
|
+
/**
|
|
88
|
+
* Build a graph from all documents in a repository.
|
|
89
|
+
* This is the main entry point — handles imports, resolution, and classification.
|
|
90
|
+
*/
|
|
91
|
+
static buildFromRepository(repository: ICanonDocumentRepository): Promise<GraphData>;
|
|
92
|
+
/**
|
|
93
|
+
* Build a graph from a single parsed document.
|
|
94
|
+
* Simpler alternative when you don't need import resolution.
|
|
95
|
+
*/
|
|
96
|
+
static buildFromDocument(document: CanonDocument): GraphData;
|
|
97
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { CanonObjectParser } from '../parsing/CanonObjectParser.js';
|
|
2
|
+
import { ResourceTypeClassifier } from '../ctl/ResourceTypeClassifier.js';
|
|
3
|
+
/**
|
|
4
|
+
* Node types in a Canon graph.
|
|
5
|
+
*/
|
|
6
|
+
export var NodeType;
|
|
7
|
+
(function (NodeType) {
|
|
8
|
+
NodeType["Class"] = "Class";
|
|
9
|
+
NodeType["DatatypeProperty"] = "DatatypeProperty";
|
|
10
|
+
NodeType["ObjectProperty"] = "ObjectProperty";
|
|
11
|
+
NodeType["AnnotationProperty"] = "AnnotationProperty";
|
|
12
|
+
NodeType["Instance"] = "Instance";
|
|
13
|
+
NodeType["Datatype"] = "Datatype";
|
|
14
|
+
NodeType["Unknown"] = "Unknown";
|
|
15
|
+
})(NodeType || (NodeType = {}));
|
|
16
|
+
/**
|
|
17
|
+
* Edge types in a Canon graph.
|
|
18
|
+
*/
|
|
19
|
+
export var EdgeType;
|
|
20
|
+
(function (EdgeType) {
|
|
21
|
+
/** Instance → Class (type) */
|
|
22
|
+
EdgeType["InstanceOf"] = "instanceOf";
|
|
23
|
+
/** Class → Class (subClassOf) */
|
|
24
|
+
EdgeType["SubClassOf"] = "subClassOf";
|
|
25
|
+
/** Property → Class (domain) */
|
|
26
|
+
EdgeType["Domain"] = "domain";
|
|
27
|
+
/** Property → Class or Datatype (range) */
|
|
28
|
+
EdgeType["Range"] = "range";
|
|
29
|
+
/** Class → Class via ObjectProperty */
|
|
30
|
+
EdgeType["ObjectRelationship"] = "objectRelationship";
|
|
31
|
+
/** Property → Property (subPropertyOf) */
|
|
32
|
+
EdgeType["SubPropertyOf"] = "subPropertyOf";
|
|
33
|
+
/** Instance → value via property */
|
|
34
|
+
EdgeType["PropertyValue"] = "propertyValue";
|
|
35
|
+
})(EdgeType || (EdgeType = {}));
|
|
36
|
+
/**
|
|
37
|
+
* Builds a visualization-ready graph from Canon documents.
|
|
38
|
+
*
|
|
39
|
+
* Usage:
|
|
40
|
+
* ```typescript
|
|
41
|
+
* import { GraphBuilder, CanonParser, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
42
|
+
*
|
|
43
|
+
* const parser = new CanonParser();
|
|
44
|
+
* const repo = new InMemoryCanonDocumentRepository(parser);
|
|
45
|
+
* const doc = parser.parse(yamlContent);
|
|
46
|
+
* await repo.saveDocumentAsync(doc, 'my-doc');
|
|
47
|
+
*
|
|
48
|
+
* const graph = await GraphBuilder.buildFromRepository(repo);
|
|
49
|
+
* // graph.nodes = [{ id, label, type, namespace, properties }, ...]
|
|
50
|
+
* // graph.edges = [{ source, target, type, label }, ...]
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export class GraphBuilder {
|
|
54
|
+
/**
|
|
55
|
+
* Build a graph from all documents in a repository.
|
|
56
|
+
* This is the main entry point — handles imports, resolution, and classification.
|
|
57
|
+
*/
|
|
58
|
+
static async buildFromRepository(repository) {
|
|
59
|
+
const objectParser = new CanonObjectParser();
|
|
60
|
+
const canons = await objectParser.parseCanons(repository);
|
|
61
|
+
const nodes = [];
|
|
62
|
+
const edges = [];
|
|
63
|
+
const knownClassNames = new Set();
|
|
64
|
+
// First pass: identify all classes
|
|
65
|
+
for (const canon of canons) {
|
|
66
|
+
const subject = canon;
|
|
67
|
+
if (!subject.name)
|
|
68
|
+
continue;
|
|
69
|
+
if (ResourceTypeClassifier.isClassType(subject)) {
|
|
70
|
+
knownClassNames.add(subject.name);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Second pass: build nodes and edges
|
|
74
|
+
for (const canon of canons) {
|
|
75
|
+
const subject = canon;
|
|
76
|
+
if (!subject.name)
|
|
77
|
+
continue;
|
|
78
|
+
const nodeType = classifyNode(subject, knownClassNames);
|
|
79
|
+
const nodeId = subject.namespace
|
|
80
|
+
? `${subject.namespace}/${subject.name}`
|
|
81
|
+
: subject.name;
|
|
82
|
+
const properties = {};
|
|
83
|
+
const entity = subject.entity ?? {};
|
|
84
|
+
// Extract properties from the raw entity for metadata
|
|
85
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
86
|
+
if (key === 'type')
|
|
87
|
+
continue; // handled by nodeType
|
|
88
|
+
properties[key] = value;
|
|
89
|
+
}
|
|
90
|
+
// Also extract from statements if available
|
|
91
|
+
if (subject.statement) {
|
|
92
|
+
for (const stmt of subject.statement) {
|
|
93
|
+
const predName = stmt.predicate?.subject?.name;
|
|
94
|
+
if (!predName || predName === 'type')
|
|
95
|
+
continue;
|
|
96
|
+
const obj = stmt.object;
|
|
97
|
+
if (obj?.subject?.name) {
|
|
98
|
+
properties[predName] = obj.subject.name;
|
|
99
|
+
}
|
|
100
|
+
else if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
|
|
101
|
+
properties[predName] = obj;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
nodes.push({
|
|
106
|
+
id: nodeId,
|
|
107
|
+
label: entity.label ?? subject.name,
|
|
108
|
+
type: nodeType,
|
|
109
|
+
namespace: subject.namespace ?? '',
|
|
110
|
+
properties,
|
|
111
|
+
});
|
|
112
|
+
// Extract edges from entity properties
|
|
113
|
+
extractEdges(nodeId, entity, nodeType, knownClassNames, edges, subject.namespace ?? '');
|
|
114
|
+
}
|
|
115
|
+
return { nodes, edges };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Build a graph from a single parsed document.
|
|
119
|
+
* Simpler alternative when you don't need import resolution.
|
|
120
|
+
*/
|
|
121
|
+
static buildFromDocument(document) {
|
|
122
|
+
const nodes = [];
|
|
123
|
+
const edges = [];
|
|
124
|
+
const namespace = document.metadata.namespace_
|
|
125
|
+
? `${document.metadata.namespace_.publisher}/${document.metadata.namespace_.package_}@${document.metadata.namespace_.version}`
|
|
126
|
+
: '';
|
|
127
|
+
const knownClassNames = new Set();
|
|
128
|
+
// First pass: identify classes
|
|
129
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
130
|
+
const type = entity?.type;
|
|
131
|
+
if (!type)
|
|
132
|
+
continue;
|
|
133
|
+
if (isClassTypeName(type)) {
|
|
134
|
+
knownClassNames.add(name);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Second pass: build graph
|
|
138
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
139
|
+
if (!entity || typeof entity !== 'object')
|
|
140
|
+
continue;
|
|
141
|
+
const typeName = entity.type;
|
|
142
|
+
const nodeType = classifyByTypeName(typeName, name, knownClassNames);
|
|
143
|
+
const nodeId = namespace ? `${namespace}/${name}` : name;
|
|
144
|
+
const properties = {};
|
|
145
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
146
|
+
if (key === 'type')
|
|
147
|
+
continue;
|
|
148
|
+
if (typeof value !== 'object' || value === null) {
|
|
149
|
+
properties[key] = value;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
nodes.push({
|
|
153
|
+
id: nodeId,
|
|
154
|
+
label: entity.label ?? name,
|
|
155
|
+
type: nodeType,
|
|
156
|
+
namespace,
|
|
157
|
+
properties,
|
|
158
|
+
});
|
|
159
|
+
extractEdges(nodeId, entity, nodeType, knownClassNames, edges, namespace);
|
|
160
|
+
}
|
|
161
|
+
return { nodes, edges };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// --- Helpers ---
|
|
165
|
+
function classifyNode(subject, knownClassNames) {
|
|
166
|
+
const s = subject;
|
|
167
|
+
if (ResourceTypeClassifier.isClassType(s))
|
|
168
|
+
return NodeType.Class;
|
|
169
|
+
if (ResourceTypeClassifier.isObjectPropertyType(s))
|
|
170
|
+
return NodeType.ObjectProperty;
|
|
171
|
+
if (ResourceTypeClassifier.isDatatypePropertyType(s))
|
|
172
|
+
return NodeType.DatatypeProperty;
|
|
173
|
+
if (ResourceTypeClassifier.isAnnotationPropertyType(s))
|
|
174
|
+
return NodeType.AnnotationProperty;
|
|
175
|
+
if (ResourceTypeClassifier.isDatatypeType(s))
|
|
176
|
+
return NodeType.Datatype;
|
|
177
|
+
if (ResourceTypeClassifier.isInstanceOfKnownClass(s, knownClassNames))
|
|
178
|
+
return NodeType.Instance;
|
|
179
|
+
return NodeType.Unknown;
|
|
180
|
+
}
|
|
181
|
+
function isClassTypeName(typeName) {
|
|
182
|
+
const name = typeName.split('.').pop() ?? typeName;
|
|
183
|
+
return name === 'Class';
|
|
184
|
+
}
|
|
185
|
+
function classifyByTypeName(typeName, entityName, knownClassNames) {
|
|
186
|
+
if (!typeName)
|
|
187
|
+
return NodeType.Unknown;
|
|
188
|
+
const name = typeName.split('.').pop() ?? typeName;
|
|
189
|
+
switch (name) {
|
|
190
|
+
case 'Class': return NodeType.Class;
|
|
191
|
+
case 'ObjectProperty': return NodeType.ObjectProperty;
|
|
192
|
+
case 'DatatypeProperty': return NodeType.DatatypeProperty;
|
|
193
|
+
case 'AnnotationProperty': return NodeType.AnnotationProperty;
|
|
194
|
+
case 'Datatype': return NodeType.Datatype;
|
|
195
|
+
default:
|
|
196
|
+
if (knownClassNames.has(name))
|
|
197
|
+
return NodeType.Instance;
|
|
198
|
+
return NodeType.Unknown;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namespace) {
|
|
202
|
+
const type = entity.type;
|
|
203
|
+
// subClassOf → SubClassOf edge
|
|
204
|
+
if (entity.subClassOf && typeof entity.subClassOf === 'string') {
|
|
205
|
+
edges.push({
|
|
206
|
+
source: sourceId,
|
|
207
|
+
target: resolveLocalId(entity.subClassOf, namespace),
|
|
208
|
+
type: EdgeType.SubClassOf,
|
|
209
|
+
label: 'subClassOf',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// subPropertyOf → SubPropertyOf edge
|
|
213
|
+
if (entity.subPropertyOf && typeof entity.subPropertyOf === 'string') {
|
|
214
|
+
edges.push({
|
|
215
|
+
source: sourceId,
|
|
216
|
+
target: resolveLocalId(entity.subPropertyOf, namespace),
|
|
217
|
+
type: EdgeType.SubPropertyOf,
|
|
218
|
+
label: 'subPropertyOf',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// domain → Domain edge (property → class)
|
|
222
|
+
if (entity.domain && typeof entity.domain === 'string') {
|
|
223
|
+
edges.push({
|
|
224
|
+
source: sourceId,
|
|
225
|
+
target: resolveLocalId(entity.domain, namespace),
|
|
226
|
+
type: EdgeType.Domain,
|
|
227
|
+
label: 'domain',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
// range → Range edge (property → class or datatype)
|
|
231
|
+
if (entity.range && typeof entity.range === 'string') {
|
|
232
|
+
edges.push({
|
|
233
|
+
source: sourceId,
|
|
234
|
+
target: resolveLocalId(entity.range, namespace),
|
|
235
|
+
type: EdgeType.Range,
|
|
236
|
+
label: 'range',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// Instance type → InstanceOf edge
|
|
240
|
+
if (nodeType === NodeType.Instance && type) {
|
|
241
|
+
const typeName = type.split('.').pop() ?? type;
|
|
242
|
+
edges.push({
|
|
243
|
+
source: sourceId,
|
|
244
|
+
target: resolveLocalId(typeName, namespace),
|
|
245
|
+
type: EdgeType.InstanceOf,
|
|
246
|
+
label: 'type',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
// ObjectProperty values on instances → PropertyValue edges
|
|
250
|
+
if (nodeType === NodeType.Instance) {
|
|
251
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
252
|
+
if (key === 'type' || typeof value !== 'string')
|
|
253
|
+
continue;
|
|
254
|
+
// If the value references a known class instance name, it's a relationship
|
|
255
|
+
// We can't always tell, but if it's not a scalar-looking value, treat it as a reference
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// For ObjectProperties, create a convenience edge showing the class→class relationship
|
|
259
|
+
if (nodeType === NodeType.ObjectProperty && entity.domain && entity.range) {
|
|
260
|
+
const domainStr = typeof entity.domain === 'string' ? entity.domain : null;
|
|
261
|
+
const rangeStr = typeof entity.range === 'string' ? entity.range : null;
|
|
262
|
+
if (domainStr && rangeStr) {
|
|
263
|
+
edges.push({
|
|
264
|
+
source: resolveLocalId(domainStr, namespace),
|
|
265
|
+
target: resolveLocalId(rangeStr, namespace),
|
|
266
|
+
type: EdgeType.ObjectRelationship,
|
|
267
|
+
label: entity.label ?? sourceId.split('/').pop() ?? '',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function resolveLocalId(name, namespace) {
|
|
273
|
+
// If name contains a slash, it's already qualified
|
|
274
|
+
if (name.includes('/'))
|
|
275
|
+
return name;
|
|
276
|
+
// If it contains a dot (alias.Entity), just use the entity part for local resolution
|
|
277
|
+
const entityName = name.includes('.') ? name : name;
|
|
278
|
+
return namespace ? `${namespace}/${entityName}` : entityName;
|
|
279
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { InMemoryCanonDocumentRepository, FileSystemCanonDocumentRepository, CompositeCanonDocumentRepository, RepositoryFactory, DocumentLocation, HttpCanonDocumentRepository, PublisherIndex, PublisherConfigResolver, getGlobalCachePath } from './repositories/index.js';
|
|
2
2
|
export type { PublisherConfig } from './repositories/index.js';
|
|
3
|
+
export { GraphBuilder, NodeType, EdgeType } from './graph/index.js';
|
|
4
|
+
export type { GraphNode, GraphEdge, GraphData } from './graph/index.js';
|
|
3
5
|
export { GitIgnoreFilter } from './filtering/index.js';
|
|
4
6
|
export type { IGitIgnoreFilter } from './filtering/index.js';
|
|
5
7
|
export { CanonParser, CanonObjectParser, PropertyMetadata } from './parsing/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { InMemoryCanonDocumentRepository, FileSystemCanonDocumentRepository, CompositeCanonDocumentRepository, RepositoryFactory, DocumentLocation, HttpCanonDocumentRepository, PublisherIndex, PublisherConfigResolver, getGlobalCachePath } from './repositories/index.js';
|
|
2
|
+
export { GraphBuilder, NodeType, EdgeType } from './graph/index.js';
|
|
2
3
|
export { GitIgnoreFilter } from './filtering/index.js';
|
|
3
4
|
export { CanonParser, CanonObjectParser, PropertyMetadata } from './parsing/index.js';
|
|
4
5
|
export { ResourceResolver, CanonUri, CanonUriBuilder, TypeResolver } from './resolution/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canon-protocol/sdk",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.5.0",
|
|
4
4
|
"description": "Canon Protocol SDK - Document repository and parsing implementations for TypeScript",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"./ctl": {
|
|
34
34
|
"types": "./dist/ctl/index.d.ts",
|
|
35
35
|
"default": "./dist/ctl/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./browser": {
|
|
38
|
+
"types": "./dist/browser.d.ts",
|
|
39
|
+
"default": "./dist/browser.js"
|
|
36
40
|
}
|
|
37
41
|
},
|
|
38
42
|
"files": [
|
|
@@ -62,7 +66,7 @@
|
|
|
62
66
|
"yaml-parser"
|
|
63
67
|
],
|
|
64
68
|
"dependencies": {
|
|
65
|
-
"@canon-protocol/types": "^8.
|
|
69
|
+
"@canon-protocol/types": "^8.5.0",
|
|
66
70
|
"ignore": "^7.0.5",
|
|
67
71
|
"js-yaml": "^4.1.0"
|
|
68
72
|
},
|