@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 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,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
+ }
@@ -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.3.0",
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.3.0",
69
+ "@canon-protocol/types": "^8.5.0",
66
70
  "ignore": "^7.0.5",
67
71
  "js-yaml": "^4.1.0"
68
72
  },