@canon-protocol/sdk 8.4.0 → 8.6.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 +290 -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,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node types in a Canon graph.
|
|
3
|
+
*/
|
|
4
|
+
export var NodeType;
|
|
5
|
+
(function (NodeType) {
|
|
6
|
+
NodeType["Class"] = "Class";
|
|
7
|
+
NodeType["DatatypeProperty"] = "DatatypeProperty";
|
|
8
|
+
NodeType["ObjectProperty"] = "ObjectProperty";
|
|
9
|
+
NodeType["AnnotationProperty"] = "AnnotationProperty";
|
|
10
|
+
NodeType["Instance"] = "Instance";
|
|
11
|
+
NodeType["Datatype"] = "Datatype";
|
|
12
|
+
NodeType["Unknown"] = "Unknown";
|
|
13
|
+
})(NodeType || (NodeType = {}));
|
|
14
|
+
/**
|
|
15
|
+
* Edge types in a Canon graph.
|
|
16
|
+
*/
|
|
17
|
+
export var EdgeType;
|
|
18
|
+
(function (EdgeType) {
|
|
19
|
+
/** Instance → Class (type) */
|
|
20
|
+
EdgeType["InstanceOf"] = "instanceOf";
|
|
21
|
+
/** Class → Class (subClassOf) */
|
|
22
|
+
EdgeType["SubClassOf"] = "subClassOf";
|
|
23
|
+
/** Property → Class (domain) */
|
|
24
|
+
EdgeType["Domain"] = "domain";
|
|
25
|
+
/** Property → Class or Datatype (range) */
|
|
26
|
+
EdgeType["Range"] = "range";
|
|
27
|
+
/** Class → Class via ObjectProperty */
|
|
28
|
+
EdgeType["ObjectRelationship"] = "objectRelationship";
|
|
29
|
+
/** Property → Property (subPropertyOf) */
|
|
30
|
+
EdgeType["SubPropertyOf"] = "subPropertyOf";
|
|
31
|
+
/** Instance → value via property */
|
|
32
|
+
EdgeType["PropertyValue"] = "propertyValue";
|
|
33
|
+
})(EdgeType || (EdgeType = {}));
|
|
34
|
+
/**
|
|
35
|
+
* Builds a visualization-ready graph from Canon documents.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* ```typescript
|
|
39
|
+
* import { GraphBuilder, CanonParser, InMemoryCanonDocumentRepository } from '@canon-protocol/sdk';
|
|
40
|
+
*
|
|
41
|
+
* const parser = new CanonParser();
|
|
42
|
+
* const repo = new InMemoryCanonDocumentRepository(parser);
|
|
43
|
+
* const doc = parser.parse(yamlContent);
|
|
44
|
+
* await repo.saveDocumentAsync(doc, 'my-doc');
|
|
45
|
+
*
|
|
46
|
+
* const graph = await GraphBuilder.buildFromRepository(repo);
|
|
47
|
+
* // graph.nodes = [{ id, label, type, namespace, properties }, ...]
|
|
48
|
+
* // graph.edges = [{ source, target, type, label }, ...]
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export class GraphBuilder {
|
|
52
|
+
/**
|
|
53
|
+
* Build a graph from all documents in a repository.
|
|
54
|
+
* This is the main entry point — handles imports, resolution, and classification.
|
|
55
|
+
*/
|
|
56
|
+
static async buildFromRepository(repository) {
|
|
57
|
+
const documents = await repository.getAllDocumentsAsync();
|
|
58
|
+
const nodes = [];
|
|
59
|
+
const edges = [];
|
|
60
|
+
const knownClassNames = new Set();
|
|
61
|
+
// Build a map from "publisher/packageName" to the full namespace string for alias resolution
|
|
62
|
+
const packageToNamespace = new Map();
|
|
63
|
+
for (const document of documents) {
|
|
64
|
+
const ns = document.metadata.namespace_;
|
|
65
|
+
if (ns) {
|
|
66
|
+
const namespace = ns.toString();
|
|
67
|
+
const packageKey = `${ns.publisher}/${ns.package_}`;
|
|
68
|
+
packageToNamespace.set(packageKey, namespace);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// First pass: identify all classes across all documents
|
|
72
|
+
for (const document of documents) {
|
|
73
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
74
|
+
const type = entity?.type;
|
|
75
|
+
if (!type)
|
|
76
|
+
continue;
|
|
77
|
+
if (isClassTypeName(type)) {
|
|
78
|
+
knownClassNames.add(name);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Second pass: build graph
|
|
83
|
+
for (const document of documents) {
|
|
84
|
+
const namespace = document.metadata.namespace_
|
|
85
|
+
? document.metadata.namespace_.toString()
|
|
86
|
+
: '';
|
|
87
|
+
const aliasMap = buildAliasMap(document, packageToNamespace);
|
|
88
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
89
|
+
if (!entity || typeof entity !== 'object')
|
|
90
|
+
continue;
|
|
91
|
+
const typeName = entity.type;
|
|
92
|
+
const nodeType = classifyByTypeName(typeName, name, knownClassNames);
|
|
93
|
+
const nodeId = namespace ? `${namespace}/${name}` : name;
|
|
94
|
+
const properties = {};
|
|
95
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
96
|
+
if (key === 'type')
|
|
97
|
+
continue;
|
|
98
|
+
if (typeof value !== 'object' || value === null) {
|
|
99
|
+
properties[key] = value;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
nodes.push({
|
|
103
|
+
id: nodeId,
|
|
104
|
+
label: entity.label ?? name,
|
|
105
|
+
type: nodeType,
|
|
106
|
+
namespace,
|
|
107
|
+
properties,
|
|
108
|
+
});
|
|
109
|
+
extractEdges(nodeId, entity, nodeType, knownClassNames, edges, namespace, aliasMap);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { nodes, edges };
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Build a graph from a single parsed document.
|
|
116
|
+
* Simpler alternative when you don't need import resolution.
|
|
117
|
+
*/
|
|
118
|
+
static buildFromDocument(document) {
|
|
119
|
+
const nodes = [];
|
|
120
|
+
const edges = [];
|
|
121
|
+
const namespace = document.metadata.namespace_
|
|
122
|
+
? `${document.metadata.namespace_.publisher}/${document.metadata.namespace_.package_}@${document.metadata.namespace_.version}`
|
|
123
|
+
: '';
|
|
124
|
+
const knownClassNames = new Set();
|
|
125
|
+
// First pass: identify classes
|
|
126
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
127
|
+
const type = entity?.type;
|
|
128
|
+
if (!type)
|
|
129
|
+
continue;
|
|
130
|
+
if (isClassTypeName(type)) {
|
|
131
|
+
knownClassNames.add(name);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Build partial alias map from document imports
|
|
135
|
+
const aliasMap = buildAliasMap(document);
|
|
136
|
+
// Second pass: build graph
|
|
137
|
+
for (const [name, entity] of Object.entries(document.body)) {
|
|
138
|
+
if (!entity || typeof entity !== 'object')
|
|
139
|
+
continue;
|
|
140
|
+
const typeName = entity.type;
|
|
141
|
+
const nodeType = classifyByTypeName(typeName, name, knownClassNames);
|
|
142
|
+
const nodeId = namespace ? `${namespace}/${name}` : name;
|
|
143
|
+
const properties = {};
|
|
144
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
145
|
+
if (key === 'type')
|
|
146
|
+
continue;
|
|
147
|
+
if (typeof value !== 'object' || value === null) {
|
|
148
|
+
properties[key] = value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
nodes.push({
|
|
152
|
+
id: nodeId,
|
|
153
|
+
label: entity.label ?? name,
|
|
154
|
+
type: nodeType,
|
|
155
|
+
namespace,
|
|
156
|
+
properties,
|
|
157
|
+
});
|
|
158
|
+
extractEdges(nodeId, entity, nodeType, knownClassNames, edges, namespace, aliasMap);
|
|
159
|
+
}
|
|
160
|
+
return { nodes, edges };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// --- Helpers ---
|
|
164
|
+
function buildAliasMap(document, packageToNamespace) {
|
|
165
|
+
const aliasMap = new Map();
|
|
166
|
+
if (document.metadata?.imports) {
|
|
167
|
+
for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
|
|
168
|
+
for (const import_ of imports) {
|
|
169
|
+
const alias = import_.alias ?? import_.packageName;
|
|
170
|
+
const packageKey = `${publisher}/${import_.packageName}`;
|
|
171
|
+
// Use resolved namespace from repository if available, otherwise approximate from import
|
|
172
|
+
const targetNamespace = packageToNamespace?.get(packageKey)
|
|
173
|
+
?? `${publisher}/${import_.packageName}@${import_.version}`;
|
|
174
|
+
aliasMap.set(alias, targetNamespace);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return aliasMap;
|
|
179
|
+
}
|
|
180
|
+
function isClassTypeName(typeName) {
|
|
181
|
+
const name = typeName.split('.').pop() ?? typeName;
|
|
182
|
+
return name === 'Class';
|
|
183
|
+
}
|
|
184
|
+
function classifyByTypeName(typeName, entityName, knownClassNames) {
|
|
185
|
+
if (!typeName)
|
|
186
|
+
return NodeType.Unknown;
|
|
187
|
+
const name = typeName.split('.').pop() ?? typeName;
|
|
188
|
+
switch (name) {
|
|
189
|
+
case 'Class': return NodeType.Class;
|
|
190
|
+
case 'ObjectProperty': return NodeType.ObjectProperty;
|
|
191
|
+
case 'DatatypeProperty': return NodeType.DatatypeProperty;
|
|
192
|
+
case 'AnnotationProperty': return NodeType.AnnotationProperty;
|
|
193
|
+
case 'Datatype': return NodeType.Datatype;
|
|
194
|
+
default:
|
|
195
|
+
if (knownClassNames.has(name))
|
|
196
|
+
return NodeType.Instance;
|
|
197
|
+
return NodeType.Unknown;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namespace, aliasMap) {
|
|
201
|
+
const type = entity.type;
|
|
202
|
+
// subClassOf → SubClassOf edge
|
|
203
|
+
if (entity.subClassOf && typeof entity.subClassOf === 'string') {
|
|
204
|
+
edges.push({
|
|
205
|
+
source: sourceId,
|
|
206
|
+
target: resolveLocalId(entity.subClassOf, namespace, aliasMap),
|
|
207
|
+
type: EdgeType.SubClassOf,
|
|
208
|
+
label: 'subClassOf',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
// subPropertyOf → SubPropertyOf edge
|
|
212
|
+
if (entity.subPropertyOf && typeof entity.subPropertyOf === 'string') {
|
|
213
|
+
edges.push({
|
|
214
|
+
source: sourceId,
|
|
215
|
+
target: resolveLocalId(entity.subPropertyOf, namespace, aliasMap),
|
|
216
|
+
type: EdgeType.SubPropertyOf,
|
|
217
|
+
label: 'subPropertyOf',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// domain → Domain edge (property → class)
|
|
221
|
+
if (entity.domain && typeof entity.domain === 'string') {
|
|
222
|
+
edges.push({
|
|
223
|
+
source: sourceId,
|
|
224
|
+
target: resolveLocalId(entity.domain, namespace, aliasMap),
|
|
225
|
+
type: EdgeType.Domain,
|
|
226
|
+
label: 'domain',
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// range → Range edge (property → class or datatype)
|
|
230
|
+
if (entity.range && typeof entity.range === 'string') {
|
|
231
|
+
edges.push({
|
|
232
|
+
source: sourceId,
|
|
233
|
+
target: resolveLocalId(entity.range, namespace, aliasMap),
|
|
234
|
+
type: EdgeType.Range,
|
|
235
|
+
label: 'range',
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// Instance type → InstanceOf edge
|
|
239
|
+
if (nodeType === NodeType.Instance && type) {
|
|
240
|
+
const typeName = type.split('.').pop() ?? type;
|
|
241
|
+
edges.push({
|
|
242
|
+
source: sourceId,
|
|
243
|
+
target: resolveLocalId(typeName, namespace, aliasMap),
|
|
244
|
+
type: EdgeType.InstanceOf,
|
|
245
|
+
label: 'type',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
// ObjectProperty values on instances → PropertyValue edges
|
|
249
|
+
if (nodeType === NodeType.Instance) {
|
|
250
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
251
|
+
if (key === 'type' || typeof value !== 'string')
|
|
252
|
+
continue;
|
|
253
|
+
// If the value references a known class instance name, it's a relationship
|
|
254
|
+
// We can't always tell, but if it's not a scalar-looking value, treat it as a reference
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// For ObjectProperties, create a convenience edge showing the class→class relationship
|
|
258
|
+
if (nodeType === NodeType.ObjectProperty && entity.domain && entity.range) {
|
|
259
|
+
const domainStr = typeof entity.domain === 'string' ? entity.domain : null;
|
|
260
|
+
const rangeStr = typeof entity.range === 'string' ? entity.range : null;
|
|
261
|
+
if (domainStr && rangeStr) {
|
|
262
|
+
edges.push({
|
|
263
|
+
source: resolveLocalId(domainStr, namespace, aliasMap),
|
|
264
|
+
target: resolveLocalId(rangeStr, namespace, aliasMap),
|
|
265
|
+
type: EdgeType.ObjectRelationship,
|
|
266
|
+
label: entity.label ?? sourceId.split('/').pop() ?? '',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function resolveLocalId(name, namespace, aliasMap) {
|
|
272
|
+
// Already qualified with a slash — return as-is
|
|
273
|
+
if (name.includes('/'))
|
|
274
|
+
return name;
|
|
275
|
+
// Handle aliased references (e.g., "sdg.gdpr")
|
|
276
|
+
if (name.includes('.')) {
|
|
277
|
+
const dotIndex = name.indexOf('.');
|
|
278
|
+
const alias = name.substring(0, dotIndex);
|
|
279
|
+
const entityName = name.substring(dotIndex + 1);
|
|
280
|
+
if (aliasMap) {
|
|
281
|
+
const targetNamespace = aliasMap.get(alias);
|
|
282
|
+
if (targetNamespace) {
|
|
283
|
+
return `${targetNamespace}/${entityName}`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Fallback: use entity name with local namespace
|
|
287
|
+
return namespace ? `${namespace}/${entityName}` : entityName;
|
|
288
|
+
}
|
|
289
|
+
return namespace ? `${namespace}/${name}` : name;
|
|
290
|
+
}
|
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.6.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.6.0",
|
|
66
70
|
"ignore": "^7.0.5",
|
|
67
71
|
"js-yaml": "^4.1.0"
|
|
68
72
|
},
|