@canon-protocol/sdk 8.5.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.
@@ -1,5 +1,3 @@
1
- import { CanonObjectParser } from '../parsing/CanonObjectParser.js';
2
- import { ResourceTypeClassifier } from '../ctl/ResourceTypeClassifier.js';
3
1
  /**
4
2
  * Node types in a Canon graph.
5
3
  */
@@ -56,61 +54,60 @@ export class GraphBuilder {
56
54
  * This is the main entry point — handles imports, resolution, and classification.
57
55
  */
58
56
  static async buildFromRepository(repository) {
59
- const objectParser = new CanonObjectParser();
60
- const canons = await objectParser.parseCanons(repository);
57
+ const documents = await repository.getAllDocumentsAsync();
61
58
  const nodes = [];
62
59
  const edges = [];
63
60
  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);
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);
71
69
  }
72
70
  }
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;
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
+ }
89
80
  }
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')
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')
95
97
  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;
98
+ if (typeof value !== 'object' || value === null) {
99
+ properties[key] = value;
102
100
  }
103
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);
104
110
  }
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
111
  }
115
112
  return { nodes, edges };
116
113
  }
@@ -134,6 +131,8 @@ export class GraphBuilder {
134
131
  knownClassNames.add(name);
135
132
  }
136
133
  }
134
+ // Build partial alias map from document imports
135
+ const aliasMap = buildAliasMap(document);
137
136
  // Second pass: build graph
138
137
  for (const [name, entity] of Object.entries(document.body)) {
139
138
  if (!entity || typeof entity !== 'object')
@@ -156,27 +155,27 @@ export class GraphBuilder {
156
155
  namespace,
157
156
  properties,
158
157
  });
159
- extractEdges(nodeId, entity, nodeType, knownClassNames, edges, namespace);
158
+ extractEdges(nodeId, entity, nodeType, knownClassNames, edges, namespace, aliasMap);
160
159
  }
161
160
  return { nodes, edges };
162
161
  }
163
162
  }
164
163
  // --- 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;
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;
180
179
  }
181
180
  function isClassTypeName(typeName) {
182
181
  const name = typeName.split('.').pop() ?? typeName;
@@ -198,13 +197,13 @@ function classifyByTypeName(typeName, entityName, knownClassNames) {
198
197
  return NodeType.Unknown;
199
198
  }
200
199
  }
201
- function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namespace) {
200
+ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namespace, aliasMap) {
202
201
  const type = entity.type;
203
202
  // subClassOf → SubClassOf edge
204
203
  if (entity.subClassOf && typeof entity.subClassOf === 'string') {
205
204
  edges.push({
206
205
  source: sourceId,
207
- target: resolveLocalId(entity.subClassOf, namespace),
206
+ target: resolveLocalId(entity.subClassOf, namespace, aliasMap),
208
207
  type: EdgeType.SubClassOf,
209
208
  label: 'subClassOf',
210
209
  });
@@ -213,7 +212,7 @@ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namesp
213
212
  if (entity.subPropertyOf && typeof entity.subPropertyOf === 'string') {
214
213
  edges.push({
215
214
  source: sourceId,
216
- target: resolveLocalId(entity.subPropertyOf, namespace),
215
+ target: resolveLocalId(entity.subPropertyOf, namespace, aliasMap),
217
216
  type: EdgeType.SubPropertyOf,
218
217
  label: 'subPropertyOf',
219
218
  });
@@ -222,7 +221,7 @@ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namesp
222
221
  if (entity.domain && typeof entity.domain === 'string') {
223
222
  edges.push({
224
223
  source: sourceId,
225
- target: resolveLocalId(entity.domain, namespace),
224
+ target: resolveLocalId(entity.domain, namespace, aliasMap),
226
225
  type: EdgeType.Domain,
227
226
  label: 'domain',
228
227
  });
@@ -231,7 +230,7 @@ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namesp
231
230
  if (entity.range && typeof entity.range === 'string') {
232
231
  edges.push({
233
232
  source: sourceId,
234
- target: resolveLocalId(entity.range, namespace),
233
+ target: resolveLocalId(entity.range, namespace, aliasMap),
235
234
  type: EdgeType.Range,
236
235
  label: 'range',
237
236
  });
@@ -241,7 +240,7 @@ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namesp
241
240
  const typeName = type.split('.').pop() ?? type;
242
241
  edges.push({
243
242
  source: sourceId,
244
- target: resolveLocalId(typeName, namespace),
243
+ target: resolveLocalId(typeName, namespace, aliasMap),
245
244
  type: EdgeType.InstanceOf,
246
245
  label: 'type',
247
246
  });
@@ -261,19 +260,31 @@ function extractEdges(sourceId, entity, nodeType, knownClassNames, edges, namesp
261
260
  const rangeStr = typeof entity.range === 'string' ? entity.range : null;
262
261
  if (domainStr && rangeStr) {
263
262
  edges.push({
264
- source: resolveLocalId(domainStr, namespace),
265
- target: resolveLocalId(rangeStr, namespace),
263
+ source: resolveLocalId(domainStr, namespace, aliasMap),
264
+ target: resolveLocalId(rangeStr, namespace, aliasMap),
266
265
  type: EdgeType.ObjectRelationship,
267
266
  label: entity.label ?? sourceId.split('/').pop() ?? '',
268
267
  });
269
268
  }
270
269
  }
271
270
  }
272
- function resolveLocalId(name, namespace) {
273
- // If name contains a slash, it's already qualified
271
+ function resolveLocalId(name, namespace, aliasMap) {
272
+ // Already qualified with a slash return as-is
274
273
  if (name.includes('/'))
275
274
  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;
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;
279
290
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canon-protocol/sdk",
3
- "version": "8.5.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",
@@ -66,7 +66,7 @@
66
66
  "yaml-parser"
67
67
  ],
68
68
  "dependencies": {
69
- "@canon-protocol/types": "^8.5.0",
69
+ "@canon-protocol/types": "^8.6.0",
70
70
  "ignore": "^7.0.5",
71
71
  "js-yaml": "^4.1.0"
72
72
  },