@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 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';
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export { GraphBuilder } from './GraphBuilder.js';
2
+ export { NodeType, EdgeType } from './GraphBuilder.js';
3
+ export type { GraphNode, GraphEdge, GraphData } from './GraphBuilder.js';
@@ -0,0 +1,2 @@
1
+ export { GraphBuilder } from './GraphBuilder.js';
2
+ export { NodeType, EdgeType } from './GraphBuilder.js';
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.4.0",
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.4.0",
69
+ "@canon-protocol/types": "^8.6.0",
66
70
  "ignore": "^7.0.5",
67
71
  "js-yaml": "^4.1.0"
68
72
  },