@canon-protocol/sdk 8.0.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.
Files changed (165) hide show
  1. package/dist/canons/Canon.d.ts +2 -0
  2. package/dist/canons/Canon.js +2 -0
  3. package/dist/canons/DefinedCanon.d.ts +5 -0
  4. package/dist/canons/DefinedCanon.js +4 -0
  5. package/dist/canons/EmbeddedCanon.d.ts +3 -0
  6. package/dist/canons/EmbeddedCanon.js +3 -0
  7. package/dist/canons/ReferenceCanon.d.ts +6 -0
  8. package/dist/canons/ReferenceCanon.js +10 -0
  9. package/dist/canons/SubjectCanon.d.ts +6 -0
  10. package/dist/canons/SubjectCanon.js +6 -0
  11. package/dist/canons/index.d.ts +5 -0
  12. package/dist/canons/index.js +5 -0
  13. package/dist/ctl/CtlCanonUri.d.ts +28 -0
  14. package/dist/ctl/CtlCanonUri.js +230 -0
  15. package/dist/ctl/CtlGraphResolver.d.ts +14 -0
  16. package/dist/ctl/CtlGraphResolver.js +411 -0
  17. package/dist/ctl/CtlMarkdownRenderer.d.ts +17 -0
  18. package/dist/ctl/CtlMarkdownRenderer.js +262 -0
  19. package/dist/ctl/CtlParser.d.ts +10 -0
  20. package/dist/ctl/CtlParser.js +331 -0
  21. package/dist/ctl/CtlValidator.d.ts +12 -0
  22. package/dist/ctl/CtlValidator.js +207 -0
  23. package/dist/ctl/ResourceTypeClassifier.d.ts +21 -0
  24. package/dist/ctl/ResourceTypeClassifier.js +126 -0
  25. package/dist/ctl/index.d.ts +12 -0
  26. package/dist/ctl/index.js +9 -0
  27. package/dist/filtering/GitIgnoreFilter.d.ts +7 -0
  28. package/dist/filtering/GitIgnoreFilter.js +32 -0
  29. package/dist/filtering/IGitIgnoreFilter.d.ts +3 -0
  30. package/dist/filtering/IGitIgnoreFilter.js +1 -0
  31. package/dist/filtering/index.d.ts +2 -0
  32. package/dist/filtering/index.js +1 -0
  33. package/dist/index.d.ts +19 -0
  34. package/dist/index.js +8 -0
  35. package/dist/parsing/CanonObjectParser.d.ts +21 -0
  36. package/dist/parsing/CanonObjectParser.js +242 -0
  37. package/dist/parsing/CanonParser.d.ts +21 -0
  38. package/dist/parsing/CanonParser.js +457 -0
  39. package/dist/parsing/ICanonObjectParser.d.ts +7 -0
  40. package/dist/parsing/ICanonObjectParser.js +1 -0
  41. package/dist/parsing/PropertyMetadata.d.ts +7 -0
  42. package/dist/parsing/PropertyMetadata.js +7 -0
  43. package/dist/parsing/index.d.ts +6 -0
  44. package/dist/parsing/index.js +3 -0
  45. package/dist/repositories/CompositeCanonDocumentRepository.d.ts +28 -0
  46. package/dist/repositories/CompositeCanonDocumentRepository.js +125 -0
  47. package/dist/repositories/FileSystemCanonDocumentRepository.d.ts +28 -0
  48. package/dist/repositories/FileSystemCanonDocumentRepository.js +295 -0
  49. package/dist/repositories/HttpCanonDocumentRepository.d.ts +24 -0
  50. package/dist/repositories/HttpCanonDocumentRepository.js +79 -0
  51. package/dist/repositories/InMemoryCanonDocumentRepository.d.ts +23 -0
  52. package/dist/repositories/InMemoryCanonDocumentRepository.js +149 -0
  53. package/dist/repositories/PublisherConfig.d.ts +12 -0
  54. package/dist/repositories/PublisherConfig.js +62 -0
  55. package/dist/repositories/PublisherIndex.d.ts +20 -0
  56. package/dist/repositories/PublisherIndex.js +127 -0
  57. package/dist/repositories/RepositoryFactory.d.ts +6 -0
  58. package/dist/repositories/RepositoryFactory.js +13 -0
  59. package/dist/repositories/index.d.ts +9 -0
  60. package/dist/repositories/index.js +7 -0
  61. package/dist/resolution/CanonUri.d.ts +10 -0
  62. package/dist/resolution/CanonUri.js +55 -0
  63. package/dist/resolution/CanonUriBuilder.d.ts +12 -0
  64. package/dist/resolution/CanonUriBuilder.js +51 -0
  65. package/dist/resolution/ResourceResolutionResult.d.ts +9 -0
  66. package/dist/resolution/ResourceResolutionResult.js +1 -0
  67. package/dist/resolution/ResourceResolver.d.ts +27 -0
  68. package/dist/resolution/ResourceResolver.js +266 -0
  69. package/dist/resolution/TypeResolver.d.ts +15 -0
  70. package/dist/resolution/TypeResolver.js +110 -0
  71. package/dist/resolution/index.d.ts +6 -0
  72. package/dist/resolution/index.js +4 -0
  73. package/dist/statements/BooleanStatement.d.ts +3 -0
  74. package/dist/statements/BooleanStatement.js +3 -0
  75. package/dist/statements/EmbeddedStatement.d.ts +4 -0
  76. package/dist/statements/EmbeddedStatement.js +3 -0
  77. package/dist/statements/IStatement.d.ts +2 -0
  78. package/dist/statements/IStatement.js +1 -0
  79. package/dist/statements/ListStatement.d.ts +4 -0
  80. package/dist/statements/ListStatement.js +3 -0
  81. package/dist/statements/NumberStatement.d.ts +4 -0
  82. package/dist/statements/NumberStatement.js +10 -0
  83. package/dist/statements/ReferenceStatement.d.ts +5 -0
  84. package/dist/statements/ReferenceStatement.js +10 -0
  85. package/dist/statements/ScalarStatement.d.ts +3 -0
  86. package/dist/statements/ScalarStatement.js +3 -0
  87. package/dist/statements/Statement.d.ts +6 -0
  88. package/dist/statements/Statement.js +4 -0
  89. package/dist/statements/StringStatement.d.ts +4 -0
  90. package/dist/statements/StringStatement.js +10 -0
  91. package/dist/statements/index.d.ts +9 -0
  92. package/dist/statements/index.js +8 -0
  93. package/dist/validation/CanonObjectValidator.d.ts +21 -0
  94. package/dist/validation/CanonObjectValidator.js +150 -0
  95. package/dist/validation/ICanonObjectValidator.d.ts +7 -0
  96. package/dist/validation/ICanonObjectValidator.js +1 -0
  97. package/dist/validation/OntologyValidationError.d.ts +15 -0
  98. package/dist/validation/OntologyValidationError.js +25 -0
  99. package/dist/validation/OntologyValidationResult.d.ts +7 -0
  100. package/dist/validation/OntologyValidationResult.js +8 -0
  101. package/dist/validation/ValidationContext.d.ts +6 -0
  102. package/dist/validation/ValidationContext.js +10 -0
  103. package/dist/validation/ValidationSeverity.d.ts +4 -0
  104. package/dist/validation/ValidationSeverity.js +5 -0
  105. package/dist/validation/index.d.ts +10 -0
  106. package/dist/validation/index.js +7 -0
  107. package/dist/validation/rules/document/EmbeddedCanonNoExplicitTypeRule.d.ts +8 -0
  108. package/dist/validation/rules/document/EmbeddedCanonNoExplicitTypeRule.js +53 -0
  109. package/dist/validation/rules/document/IDocumentValidationRule.d.ts +6 -0
  110. package/dist/validation/rules/document/IDocumentValidationRule.js +1 -0
  111. package/dist/validation/rules/document/NamespacePrefixRule.d.ts +10 -0
  112. package/dist/validation/rules/document/NamespacePrefixRule.js +51 -0
  113. package/dist/validation/rules/document/PropertyTypeSpecificityRule.d.ts +11 -0
  114. package/dist/validation/rules/document/PropertyTypeSpecificityRule.js +89 -0
  115. package/dist/validation/rules/document/ResourceNamingRule.d.ts +12 -0
  116. package/dist/validation/rules/document/ResourceNamingRule.js +79 -0
  117. package/dist/validation/rules/document/SubjectCanonTypeRequiredRule.d.ts +7 -0
  118. package/dist/validation/rules/document/SubjectCanonTypeRequiredRule.js +33 -0
  119. package/dist/validation/rules/document/index.d.ts +6 -0
  120. package/dist/validation/rules/document/index.js +5 -0
  121. package/dist/validation/rules/normalizeToStringList.d.ts +1 -0
  122. package/dist/validation/rules/normalizeToStringList.js +9 -0
  123. package/dist/validation/rules/repository/AmbiguousReferenceRule.d.ts +14 -0
  124. package/dist/validation/rules/repository/AmbiguousReferenceRule.js +155 -0
  125. package/dist/validation/rules/repository/ClassDefinitionRule.d.ts +12 -0
  126. package/dist/validation/rules/repository/ClassDefinitionRule.js +205 -0
  127. package/dist/validation/rules/repository/ClassHierarchyCycleRule.d.ts +11 -0
  128. package/dist/validation/rules/repository/ClassHierarchyCycleRule.js +98 -0
  129. package/dist/validation/rules/repository/DefinitionPropertyReferenceRule.d.ts +13 -0
  130. package/dist/validation/rules/repository/DefinitionPropertyReferenceRule.js +162 -0
  131. package/dist/validation/rules/repository/IRepositoryValidationRule.d.ts +7 -0
  132. package/dist/validation/rules/repository/IRepositoryValidationRule.js +1 -0
  133. package/dist/validation/rules/repository/ImportExistenceRule.d.ts +12 -0
  134. package/dist/validation/rules/repository/ImportExistenceRule.js +124 -0
  135. package/dist/validation/rules/repository/InstancePropertyReferenceRule.d.ts +13 -0
  136. package/dist/validation/rules/repository/InstancePropertyReferenceRule.js +161 -0
  137. package/dist/validation/rules/repository/NamespaceImportCycleRule.d.ts +11 -0
  138. package/dist/validation/rules/repository/NamespaceImportCycleRule.js +85 -0
  139. package/dist/validation/rules/repository/ObjectPropertyImportRule.d.ts +9 -0
  140. package/dist/validation/rules/repository/ObjectPropertyImportRule.js +113 -0
  141. package/dist/validation/rules/repository/ObjectPropertyValueValidationRule.d.ts +12 -0
  142. package/dist/validation/rules/repository/ObjectPropertyValueValidationRule.js +281 -0
  143. package/dist/validation/rules/repository/PropertyDomainRule.d.ts +14 -0
  144. package/dist/validation/rules/repository/PropertyDomainRule.js +221 -0
  145. package/dist/validation/rules/repository/PropertyHierarchyCycleRule.d.ts +11 -0
  146. package/dist/validation/rules/repository/PropertyHierarchyCycleRule.js +104 -0
  147. package/dist/validation/rules/repository/PropertyRangeReferenceRule.d.ts +13 -0
  148. package/dist/validation/rules/repository/PropertyRangeReferenceRule.js +184 -0
  149. package/dist/validation/rules/repository/PropertyRangeRequiredRule.d.ts +8 -0
  150. package/dist/validation/rules/repository/PropertyRangeRequiredRule.js +40 -0
  151. package/dist/validation/rules/repository/PropertyValueTypeRule.d.ts +11 -0
  152. package/dist/validation/rules/repository/PropertyValueTypeRule.js +171 -0
  153. package/dist/validation/rules/repository/SubClassOfReferenceRule.d.ts +11 -0
  154. package/dist/validation/rules/repository/SubClassOfReferenceRule.js +133 -0
  155. package/dist/validation/rules/repository/SubPropertyOfReferenceRule.d.ts +11 -0
  156. package/dist/validation/rules/repository/SubPropertyOfReferenceRule.js +133 -0
  157. package/dist/validation/rules/repository/TypeAmbiguityRule.d.ts +10 -0
  158. package/dist/validation/rules/repository/TypeAmbiguityRule.js +104 -0
  159. package/dist/validation/rules/repository/UnresolvedReferenceRule.d.ts +11 -0
  160. package/dist/validation/rules/repository/UnresolvedReferenceRule.js +91 -0
  161. package/dist/validation/rules/repository/XsdImportRule.d.ts +11 -0
  162. package/dist/validation/rules/repository/XsdImportRule.js +125 -0
  163. package/dist/validation/rules/repository/index.d.ts +20 -0
  164. package/dist/validation/rules/repository/index.js +19 -0
  165. package/package.json +82 -0
@@ -0,0 +1,411 @@
1
+ import { ResolvedResourceType } from '@canon-protocol/types/ctl/resolution/enums';
2
+ import { ResourceTypeClassifier } from './ResourceTypeClassifier.js';
3
+ const CORE_PACKAGES = new Set([
4
+ 'canon-protocol.org/core-rdf',
5
+ 'canon-protocol.org/core-rdfs',
6
+ 'canon-protocol.org/core-owl',
7
+ 'canon-protocol.org/core-xsd',
8
+ 'canon-protocol.org/core-canon'
9
+ ]);
10
+ var EntityTier;
11
+ (function (EntityTier) {
12
+ EntityTier[EntityTier["Direct"] = 0] = "Direct";
13
+ EntityTier[EntityTier["Inherited"] = 1] = "Inherited";
14
+ EntityTier[EntityTier["Foundation"] = 2] = "Foundation";
15
+ })(EntityTier || (EntityTier = {}));
16
+ export class CtlGraphResolver {
17
+ repository;
18
+ constructor(repository) {
19
+ if (!repository) {
20
+ throw new Error('Repository is required for CTL graph resolution');
21
+ }
22
+ this.repository = repository;
23
+ }
24
+ async resolveAsync(document, maxDepth = 1000) {
25
+ if (!document) {
26
+ throw new Error('Document cannot be null');
27
+ }
28
+ const visited = new Set();
29
+ const directRefs = [];
30
+ const inheritedRefs = [];
31
+ const foundationRefs = [];
32
+ const unresolved = [];
33
+ const directRefUris = new Set();
34
+ for (const reference of document.references) {
35
+ if (reference.parsedUri) {
36
+ directRefUris.add(reference.parsedUri.toString());
37
+ }
38
+ }
39
+ const entityLookup = new Map();
40
+ const entitiesByNamespace = new Map();
41
+ const allDocs = await this.repository.getAllDocumentsAsync();
42
+ for (const doc of allDocs) {
43
+ const ns = doc.metadata?.namespace_?.toString();
44
+ if (!ns || !doc.body)
45
+ continue;
46
+ const nsEntities = [];
47
+ for (const [entityName, entityDef] of Object.entries(doc.body)) {
48
+ if (entityDef && typeof entityDef === 'object' && !Array.isArray(entityDef)) {
49
+ const mockEntity = this.createMockSubjectCanon(entityName, entityDef, ns);
50
+ const key = `canon://${ns}/${entityName}`;
51
+ if (!entityLookup.has(key)) {
52
+ entityLookup.set(key, { entity: mockEntity, namespace: ns });
53
+ }
54
+ nsEntities.push(mockEntity);
55
+ }
56
+ }
57
+ entitiesByNamespace.set(ns, nsEntities);
58
+ }
59
+ let maxDepthReached = 0;
60
+ let wasTruncated = false;
61
+ for (const reference of document.references) {
62
+ if (!reference.parsedUri) {
63
+ unresolved.push({
64
+ reference: reference.rawUri,
65
+ reason: reference.error ?? 'Failed to parse URI'
66
+ });
67
+ continue;
68
+ }
69
+ const canonUri = reference.parsedUri.toString();
70
+ if (visited.has(canonUri))
71
+ continue;
72
+ const depthReached = await this.resolveEntityRecursive(canonUri, null, null, 0, maxDepth, visited, directRefUris, directRefs, inheritedRefs, foundationRefs, unresolved, entityLookup, entitiesByNamespace);
73
+ maxDepthReached = Math.max(maxDepthReached, depthReached);
74
+ if (depthReached >= maxDepth)
75
+ wasTruncated = true;
76
+ }
77
+ const title = extractTitle(document.rawMarkdown);
78
+ return {
79
+ sourceFile: document.filePath ?? null,
80
+ originalContent: document.rawMarkdown,
81
+ title,
82
+ directReferences: directRefs,
83
+ inheritedReferences: inheritedRefs,
84
+ foundationTypes: foundationRefs,
85
+ get totalResourceCount() {
86
+ return directRefs.length + inheritedRefs.length + foundationRefs.length;
87
+ },
88
+ get allResources() {
89
+ return [...directRefs, ...inheritedRefs, ...foundationRefs];
90
+ },
91
+ maxDepthReached,
92
+ wasTruncated,
93
+ unresolvedResources: unresolved
94
+ };
95
+ }
96
+ async resolveEntityRecursive(canonUri, entity, sourceDocument, depth, maxDepth, visited, directRefUris, directRefs, inheritedRefs, foundationRefs, unresolved, entityLookup, entitiesByNamespace) {
97
+ if (visited.has(canonUri))
98
+ return depth;
99
+ visited.add(canonUri);
100
+ if (depth >= maxDepth)
101
+ return depth;
102
+ if (!entity) {
103
+ const lookup = entityLookup.get(canonUri);
104
+ if (!lookup) {
105
+ unresolved.push({
106
+ reference: canonUri,
107
+ reason: 'Resource not found in workspace'
108
+ });
109
+ return depth;
110
+ }
111
+ entity = lookup.entity;
112
+ }
113
+ const entityAny = entity;
114
+ const entityNamespace = entityAny.namespace ?? entityAny.namespace_ ?? '';
115
+ if (!sourceDocument && entityNamespace) {
116
+ sourceDocument = await this.repository.getDocumentAsync(entityNamespace);
117
+ }
118
+ const resolved = this.createResolvedResource(entity, canonUri, depth, entitiesByNamespace);
119
+ const tier = this.classifyTier(canonUri, entityNamespace, directRefUris);
120
+ switch (tier) {
121
+ case EntityTier.Direct:
122
+ directRefs.push(resolved);
123
+ break;
124
+ case EntityTier.Foundation:
125
+ foundationRefs.push(resolved);
126
+ break;
127
+ default:
128
+ inheritedRefs.push(resolved);
129
+ break;
130
+ }
131
+ let maxDepthReached = depth;
132
+ for (const parentClass of resolved.parentClasses) {
133
+ const parentUri = this.resolveReferenceToUri(parentClass, entityNamespace, sourceDocument);
134
+ if (parentUri && !visited.has(parentUri)) {
135
+ const lookup = entityLookup.get(parentUri);
136
+ const depthReached = await this.resolveEntityRecursive(parentUri, lookup?.entity ?? null, null, // Let it fetch its own source document
137
+ depth + 1, maxDepth, visited, directRefUris, directRefs, inheritedRefs, foundationRefs, unresolved, entityLookup, entitiesByNamespace);
138
+ maxDepthReached = Math.max(maxDepthReached, depthReached);
139
+ }
140
+ }
141
+ for (const property of resolved.properties.filter((p) => p.isObjectProperty)) {
142
+ const rangeUri = this.resolveReferenceToUri(property.rangeType, entityNamespace, sourceDocument);
143
+ if (rangeUri && !visited.has(rangeUri)) {
144
+ const lookup = entityLookup.get(rangeUri);
145
+ const depthReached = await this.resolveEntityRecursive(rangeUri, lookup?.entity ?? null, null, depth + 1, maxDepth, visited, directRefUris, directRefs, inheritedRefs, foundationRefs, unresolved, entityLookup, entitiesByNamespace);
146
+ maxDepthReached = Math.max(maxDepthReached, depthReached);
147
+ }
148
+ }
149
+ if (resolved.instanceOf_) {
150
+ const typeUri = this.resolveReferenceToUri(resolved.instanceOf_, entityNamespace, sourceDocument);
151
+ if (typeUri && !visited.has(typeUri)) {
152
+ const lookup = entityLookup.get(typeUri);
153
+ const depthReached = await this.resolveEntityRecursive(typeUri, lookup?.entity ?? null, null, depth + 1, maxDepth, visited, directRefUris, directRefs, inheritedRefs, foundationRefs, unresolved, entityLookup, entitiesByNamespace);
154
+ maxDepthReached = Math.max(maxDepthReached, depthReached);
155
+ }
156
+ }
157
+ return maxDepthReached;
158
+ }
159
+ createResolvedResource(entity, canonUri, depth, entitiesByNamespace) {
160
+ const entityAny = entity;
161
+ const entityName = entityAny.name ?? '';
162
+ const entityNamespace = entityAny.namespace ?? entityAny.namespace_ ?? '';
163
+ const resourceType = this.classifyEntityType(entity);
164
+ const comment = getComment(entity);
165
+ const parentClasses = getParentClasses(entity);
166
+ const instanceOf = getInstanceOf(entity);
167
+ let properties = [];
168
+ if (resourceType === ResolvedResourceType.Class) {
169
+ const nsEntities = entitiesByNamespace.get(entityNamespace);
170
+ if (nsEntities) {
171
+ properties = extractPropertiesForClass(entityName, nsEntities);
172
+ }
173
+ }
174
+ return {
175
+ name: entityName,
176
+ canonUri,
177
+ resource: entity,
178
+ resourceType,
179
+ parentClass: parentClasses.length > 0 ? parentClasses[0] : null,
180
+ parentClasses,
181
+ comment,
182
+ properties,
183
+ namespace_: entityNamespace,
184
+ resolutionDepth: depth,
185
+ instanceOf_: instanceOf
186
+ };
187
+ }
188
+ classifyEntityType(entity) {
189
+ if (ResourceTypeClassifier.isClassType(entity))
190
+ return ResolvedResourceType.Class;
191
+ if (ResourceTypeClassifier.isAnnotationPropertyType(entity))
192
+ return ResolvedResourceType.AnnotationProperty;
193
+ if (ResourceTypeClassifier.isDatatypePropertyType(entity))
194
+ return ResolvedResourceType.DatatypeProperty;
195
+ if (ResourceTypeClassifier.isObjectPropertyType(entity))
196
+ return ResolvedResourceType.ObjectProperty;
197
+ const typeUri = ResourceTypeClassifier.getTypeUri(entity);
198
+ if (typeUri && !ResourceTypeClassifier.isCoreOntologyType(typeUri)) {
199
+ return ResolvedResourceType.Instance;
200
+ }
201
+ return ResolvedResourceType.Other;
202
+ }
203
+ classifyTier(canonUri, entityNamespace, directRefUris) {
204
+ if (directRefUris.has(canonUri))
205
+ return EntityTier.Direct;
206
+ if (isCoreOntologyEntity(entityNamespace))
207
+ return EntityTier.Foundation;
208
+ return EntityTier.Inherited;
209
+ }
210
+ resolveReferenceToUri(reference, currentNamespace, sourceDocument) {
211
+ if (reference.toLowerCase().startsWith('canon://'))
212
+ return reference;
213
+ if (reference.includes('.') && sourceDocument?.metadata) {
214
+ const dotIndex = reference.indexOf('.');
215
+ const alias = reference.substring(0, dotIndex);
216
+ const name = reference.substring(dotIndex + 1);
217
+ if (name) {
218
+ const allImports = sourceDocument.metadata.allImports;
219
+ if (allImports) {
220
+ for (const imp of allImports) {
221
+ const importAlias = imp.alias ?? imp.packageName;
222
+ if (importAlias.toLowerCase() === alias.toLowerCase()) {
223
+ const ns = imp.version
224
+ ? `${imp.publisher}/${imp.packageName}@${imp.version}`
225
+ : `${imp.publisher}/${imp.packageName}`;
226
+ return `canon://${ns}/${name}`;
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ if (!reference.includes('.') && currentNamespace) {
233
+ return `canon://${currentNamespace}/${reference}`;
234
+ }
235
+ return null;
236
+ }
237
+ createMockSubjectCanon(name, entityDef, namespace) {
238
+ const statements = [];
239
+ for (const [key, value] of Object.entries(entityDef)) {
240
+ if (typeof value === 'string') {
241
+ if (key === 'type' || key === 'subClassOf' || key === 'domain' || key === 'range' || key === 'subPropertyOf') {
242
+ statements.push({
243
+ predicate: { subject: { name: key, publisher: '', package_: '' } },
244
+ object: { subject: { name: value, publisher: '', package_: '' } }
245
+ });
246
+ }
247
+ else {
248
+ statements.push({
249
+ predicate: { subject: { name: key, publisher: '', package_: '' } },
250
+ object: value
251
+ });
252
+ }
253
+ }
254
+ else if (Array.isArray(value)) {
255
+ statements.push({
256
+ predicate: { subject: { name: key, publisher: '', package_: '' } },
257
+ object: value.map((item) => {
258
+ if (typeof item === 'string') {
259
+ return { subject: { name: item, publisher: '', package_: '' } };
260
+ }
261
+ return item;
262
+ })
263
+ });
264
+ }
265
+ }
266
+ return {
267
+ name,
268
+ namespace_: namespace,
269
+ statement: statements,
270
+ entity: entityDef
271
+ };
272
+ }
273
+ }
274
+ function isCoreOntologyEntity(namespaceStr) {
275
+ if (!namespaceStr)
276
+ return false;
277
+ const parts = namespaceStr.split('/');
278
+ if (parts.length < 2)
279
+ return false;
280
+ const publisher = parts[0];
281
+ const packageWithVersion = parts[1];
282
+ const package_ = packageWithVersion.includes('@')
283
+ ? packageWithVersion.split('@')[0]
284
+ : packageWithVersion;
285
+ return CORE_PACKAGES.has(`${publisher}/${package_}`);
286
+ }
287
+ function getComment(entity) {
288
+ const entityAny = entity;
289
+ if (entityAny.entity && typeof entityAny.entity === 'object') {
290
+ return entityAny.entity['comment'] ?? null;
291
+ }
292
+ if (entityAny.statement && Array.isArray(entityAny.statement)) {
293
+ for (const stmt of entityAny.statement) {
294
+ if (stmt.predicate?.subject?.name === 'comment' && typeof stmt.object === 'string') {
295
+ return stmt.object;
296
+ }
297
+ }
298
+ }
299
+ return null;
300
+ }
301
+ function getParentClasses(entity) {
302
+ const parents = [];
303
+ const entityAny = entity;
304
+ const entityName = entityAny.name ?? '';
305
+ if (entityAny.entity && typeof entityAny.entity === 'object') {
306
+ const subClassOf = entityAny.entity['subClassOf'];
307
+ if (typeof subClassOf === 'string') {
308
+ if (subClassOf.toLowerCase() !== entityName.toLowerCase()) {
309
+ parents.push(subClassOf);
310
+ }
311
+ }
312
+ else if (Array.isArray(subClassOf)) {
313
+ for (const item of subClassOf) {
314
+ const name = typeof item === 'string' ? item : item?.subject?.name ?? item?.name;
315
+ if (name && name.toLowerCase() !== entityName.toLowerCase()) {
316
+ parents.push(name);
317
+ }
318
+ }
319
+ }
320
+ return parents;
321
+ }
322
+ if (entityAny.statement && Array.isArray(entityAny.statement)) {
323
+ for (const stmt of entityAny.statement) {
324
+ if (stmt.predicate?.subject?.name === 'subClassOf') {
325
+ if (stmt.object?.subject?.name) {
326
+ const name = stmt.object.subject.name;
327
+ if (name.toLowerCase() !== entityName.toLowerCase()) {
328
+ parents.push(name);
329
+ }
330
+ }
331
+ else if (Array.isArray(stmt.object)) {
332
+ for (const item of stmt.object) {
333
+ const name = item?.subject?.name ?? item?.name;
334
+ if (name && name.toLowerCase() !== entityName.toLowerCase()) {
335
+ parents.push(name);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ }
341
+ }
342
+ return parents;
343
+ }
344
+ function getInstanceOf(entity) {
345
+ const typeUri = ResourceTypeClassifier.getTypeUri(entity);
346
+ if (!typeUri)
347
+ return null;
348
+ if (!ResourceTypeClassifier.isCoreOntologyType(typeUri)) {
349
+ return typeUri.name;
350
+ }
351
+ return null;
352
+ }
353
+ function extractPropertiesForClass(className, nsEntities) {
354
+ const properties = [];
355
+ for (const entity of nsEntities) {
356
+ if (!ResourceTypeClassifier.isAnyPropertyType(entity))
357
+ continue;
358
+ const entityAny = entity;
359
+ const domains = [];
360
+ const rangeTypes = [];
361
+ if (entityAny.entity && typeof entityAny.entity === 'object') {
362
+ const domainVal = entityAny.entity['domain'];
363
+ if (typeof domainVal === 'string') {
364
+ domains.push(domainVal);
365
+ }
366
+ else if (Array.isArray(domainVal)) {
367
+ for (const d of domainVal) {
368
+ domains.push(typeof d === 'string' ? d : d?.name ?? String(d));
369
+ }
370
+ }
371
+ const rangeVal = entityAny.entity['range'];
372
+ if (typeof rangeVal === 'string') {
373
+ rangeTypes.push(rangeVal);
374
+ }
375
+ else if (Array.isArray(rangeVal)) {
376
+ for (const r of rangeVal) {
377
+ rangeTypes.push(typeof r === 'string' ? r : r?.name ?? String(r));
378
+ }
379
+ }
380
+ }
381
+ if (!domains.some((d) => d.toLowerCase() === className.toLowerCase()))
382
+ continue;
383
+ if (rangeTypes.length === 0)
384
+ rangeTypes.push('unknown');
385
+ const isObjectProperty = ResourceTypeClassifier.isObjectPropertyType(entity);
386
+ const isCollection = entityAny.entity?.['collection'] != null;
387
+ const comment = getComment(entity);
388
+ properties.push({
389
+ name: entityAny.name ?? '',
390
+ rangeTypes,
391
+ domains,
392
+ get rangeType() {
393
+ return rangeTypes.length > 0 ? rangeTypes.join(', ') : 'unknown';
394
+ },
395
+ isObjectProperty,
396
+ isCollection,
397
+ comment
398
+ });
399
+ }
400
+ return properties;
401
+ }
402
+ function extractTitle(markdown) {
403
+ const lines = markdown.split('\n');
404
+ for (const line of lines) {
405
+ const trimmed = line.trim();
406
+ if (trimmed.startsWith('# ') && !trimmed.startsWith('## ')) {
407
+ return trimmed.substring(2).trim();
408
+ }
409
+ }
410
+ return null;
411
+ }
@@ -0,0 +1,17 @@
1
+ import type { ResolvedResourceGraph } from '@canon-protocol/types/ctl/resolution/types';
2
+ import type { CtlMarkdownRenderer as ICtlMarkdownRenderer } from '@canon-protocol/types/exporters/ctl/markdown/types';
3
+ export declare class CtlMarkdownRenderer implements ICtlMarkdownRenderer {
4
+ private resolvedResourceNames;
5
+ render(graph: ResolvedResourceGraph): string;
6
+ private generateAnchor;
7
+ private renderInternalLink;
8
+ private renderInternalLinks;
9
+ private transformTemplateLinks;
10
+ private renderSummary;
11
+ private renderQuickReference;
12
+ private renderHierarchyDiagram;
13
+ private renderTier;
14
+ private renderEntity;
15
+ private renderPropertiesTable;
16
+ private renderUnresolved;
17
+ }
@@ -0,0 +1,262 @@
1
+ import { ResolvedResourceType } from '@canon-protocol/types/ctl/resolution/enums';
2
+ export class CtlMarkdownRenderer {
3
+ resolvedResourceNames = new Set();
4
+ render(graph) {
5
+ if (!graph) {
6
+ throw new Error('Graph cannot be null');
7
+ }
8
+ this.resolvedResourceNames = new Set(graph.allResources.map((e) => e.name.toLowerCase()));
9
+ const lines = [];
10
+ lines.push(this.transformTemplateLinks(graph.originalContent));
11
+ lines.push('');
12
+ lines.push('---');
13
+ lines.push('');
14
+ lines.push('# Canon Resource Reference');
15
+ lines.push('');
16
+ this.renderSummary(lines, graph);
17
+ this.renderHierarchyDiagram(lines, graph);
18
+ this.renderQuickReference(lines, graph);
19
+ this.renderTier(lines, 'Direct References', graph.directReferences, 'Resources explicitly referenced in this template.');
20
+ this.renderTier(lines, 'Inherited References', graph.inheritedReferences, 'Parent classes, property types, and other dependencies of directly referenced entities.');
21
+ this.renderTier(lines, 'Foundation Types', graph.foundationTypes, 'Core ontology types that form the base of the type hierarchy.');
22
+ if (graph.unresolvedResources.length > 0) {
23
+ this.renderUnresolved(lines, graph.unresolvedResources);
24
+ }
25
+ return lines.join('\n');
26
+ }
27
+ generateAnchor(entityName) {
28
+ return entityName.toLowerCase();
29
+ }
30
+ renderInternalLink(entityName) {
31
+ if (this.resolvedResourceNames.has(entityName.toLowerCase())) {
32
+ return `[${entityName}](#${this.generateAnchor(entityName)})`;
33
+ }
34
+ return entityName;
35
+ }
36
+ renderInternalLinks(entityNames) {
37
+ return entityNames.map((n) => this.renderInternalLink(n)).join(', ');
38
+ }
39
+ transformTemplateLinks(content) {
40
+ const pattern = /\[([^\]]+)\]\(can:\/\/([^)]+)\)/g;
41
+ return content.replace(pattern, (_match, linkText, canUri) => {
42
+ let entityName;
43
+ if (canUri.includes('#')) {
44
+ entityName = canUri.substring(canUri.indexOf('#') + 1);
45
+ }
46
+ else {
47
+ const lastDot = canUri.lastIndexOf('.');
48
+ entityName = lastDot >= 0 ? canUri.substring(lastDot + 1) : canUri;
49
+ }
50
+ if (this.resolvedResourceNames.has(entityName.toLowerCase())) {
51
+ return `[${linkText}](#${this.generateAnchor(entityName)})`;
52
+ }
53
+ return _match;
54
+ });
55
+ }
56
+ renderSummary(lines, graph) {
57
+ lines.push(`**Total Resources**: ${graph.totalResourceCount}`);
58
+ lines.push(`- Direct: ${graph.directReferences.length}`);
59
+ lines.push(`- Inherited: ${graph.inheritedReferences.length}`);
60
+ lines.push(`- Foundation: ${graph.foundationTypes.length}`);
61
+ if (graph.wasTruncated) {
62
+ lines.push('');
63
+ lines.push(`**Resolution was truncated at depth ${graph.maxDepthReached}** - some resources may be missing.`);
64
+ lines.push('To include more resources, re-run with a higher depth limit: `canon ctl resolve <file> --depth 2000`');
65
+ }
66
+ lines.push('');
67
+ }
68
+ renderQuickReference(lines, graph) {
69
+ const allEntities = graph.allResources;
70
+ if (allEntities.length === 0)
71
+ return;
72
+ lines.push('## Quick Reference');
73
+ lines.push('');
74
+ const classes = allEntities.filter((e) => e.resourceType === ResolvedResourceType.Class);
75
+ const annotationProps = allEntities.filter((e) => e.resourceType === ResolvedResourceType.AnnotationProperty);
76
+ const datatypeProps = allEntities.filter((e) => e.resourceType === ResolvedResourceType.DatatypeProperty);
77
+ const objectProps = allEntities.filter((e) => e.resourceType === ResolvedResourceType.ObjectProperty);
78
+ const instances = allEntities.filter((e) => e.resourceType === ResolvedResourceType.Instance);
79
+ if (classes.length > 0) {
80
+ lines.push(`### Classes (${classes.length})`);
81
+ lines.push('');
82
+ lines.push('| Class | Extends | Properties |');
83
+ lines.push('|-------|---------|------------|');
84
+ for (const cls of [...classes].sort((a, b) => a.name.localeCompare(b.name))) {
85
+ const link = this.renderInternalLink(cls.name);
86
+ const extends_ = cls.parentClasses.length > 0
87
+ ? this.renderInternalLinks(cls.parentClasses)
88
+ : '-';
89
+ lines.push(`| ${link} | ${extends_} | ${cls.properties.length} |`);
90
+ }
91
+ lines.push('');
92
+ }
93
+ const allProps = [...annotationProps, ...datatypeProps, ...objectProps];
94
+ if (allProps.length > 0) {
95
+ lines.push(`### Properties (${allProps.length})`);
96
+ lines.push('');
97
+ lines.push('| Property | Type | Description |');
98
+ lines.push('|----------|------|-------------|');
99
+ for (const prop of [...allProps].sort((a, b) => a.name.localeCompare(b.name))) {
100
+ const link = this.renderInternalLink(prop.name);
101
+ const propType = formatEntityType(prop.resourceType);
102
+ const desc = truncateForTable(prop.comment ?? '');
103
+ lines.push(`| ${link} | ${propType} | ${desc} |`);
104
+ }
105
+ lines.push('');
106
+ }
107
+ if (instances.length > 0) {
108
+ const instancesByType = new Map();
109
+ for (const inst of instances) {
110
+ const key = inst.instanceOf_ ?? 'Unknown';
111
+ if (!instancesByType.has(key))
112
+ instancesByType.set(key, []);
113
+ instancesByType.get(key).push(inst);
114
+ }
115
+ lines.push('### Resources by Type');
116
+ lines.push('');
117
+ lines.push('| Type | Resources |');
118
+ lines.push('|------|-----------|');
119
+ for (const [typeName, insts] of [...instancesByType.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
120
+ const typeLink = this.renderInternalLink(typeName);
121
+ const instanceLinks = this.renderInternalLinks(insts.map((i) => i.name));
122
+ lines.push(`| ${typeLink} | ${instanceLinks} |`);
123
+ }
124
+ lines.push('');
125
+ }
126
+ }
127
+ renderHierarchyDiagram(lines, graph) {
128
+ const classes = graph.allResources.filter((e) => e.resourceType === ResolvedResourceType.Class);
129
+ if (classes.length === 0)
130
+ return;
131
+ const classesWithRelations = classes.filter((c) => c.parentClasses.length > 0);
132
+ if (classesWithRelations.length === 0)
133
+ return;
134
+ const allParentNames = new Set();
135
+ for (const c of classesWithRelations) {
136
+ for (const p of c.parentClasses)
137
+ allParentNames.add(p.toLowerCase());
138
+ }
139
+ const classNames = new Set(classes.map((c) => c.name.toLowerCase()));
140
+ lines.push('## Type Hierarchy');
141
+ lines.push('');
142
+ lines.push('```mermaid');
143
+ lines.push('flowchart TB');
144
+ for (const cls of [...classes].sort((a, b) => a.name.localeCompare(b.name))) {
145
+ const safeId = makeMermaidSafeId(cls.name);
146
+ lines.push(` ${safeId}["${cls.name}"]`);
147
+ }
148
+ for (const parentName of [...allParentNames]) {
149
+ if (!classNames.has(parentName)) {
150
+ let originalName = parentName;
151
+ for (const c of classesWithRelations) {
152
+ for (const p of c.parentClasses) {
153
+ if (p.toLowerCase() === parentName)
154
+ originalName = p;
155
+ }
156
+ }
157
+ const safeId = makeMermaidSafeId(originalName);
158
+ lines.push(` ${safeId}["${originalName}"]:::external`);
159
+ }
160
+ }
161
+ lines.push('');
162
+ for (const cls of classesWithRelations) {
163
+ const childId = makeMermaidSafeId(cls.name);
164
+ for (const parent of cls.parentClasses) {
165
+ const parentId = makeMermaidSafeId(parent);
166
+ lines.push(` ${parentId} --> ${childId}`);
167
+ }
168
+ }
169
+ lines.push('');
170
+ lines.push(' classDef external fill:#f9f9f9,stroke:#999,stroke-dasharray: 5 5');
171
+ lines.push('```');
172
+ lines.push('');
173
+ }
174
+ renderTier(lines, title, entities, description) {
175
+ if (entities.length === 0)
176
+ return;
177
+ lines.push(`## ${title} (${entities.length})`);
178
+ lines.push('');
179
+ lines.push(`*${description}*`);
180
+ lines.push('');
181
+ for (const entity of [...entities].sort((a, b) => a.name.localeCompare(b.name))) {
182
+ this.renderEntity(lines, entity);
183
+ }
184
+ }
185
+ renderEntity(lines, entity) {
186
+ const anchor = this.generateAnchor(entity.name);
187
+ lines.push(`### ${entity.name}`);
188
+ lines.push(`<a id="${anchor}"></a>`);
189
+ lines.push('');
190
+ lines.push('| Attribute | Value |');
191
+ lines.push('|-----------|-------|');
192
+ lines.push(`| **URI** | \`${entity.canonUri}\` |`);
193
+ lines.push(`| **Type** | ${formatEntityType(entity.resourceType)} |`);
194
+ if (entity.parentClasses.length > 0) {
195
+ const parentLinks = this.renderInternalLinks(entity.parentClasses);
196
+ lines.push(`| **Extends** | ${parentLinks} |`);
197
+ }
198
+ if (entity.instanceOf_) {
199
+ const instanceLink = this.renderInternalLink(entity.instanceOf_);
200
+ lines.push(`| **Instance of** | ${instanceLink} |`);
201
+ }
202
+ lines.push('');
203
+ if (entity.comment) {
204
+ lines.push(entity.comment);
205
+ lines.push('');
206
+ }
207
+ if (entity.resourceType === ResolvedResourceType.Class && entity.properties.length > 0) {
208
+ this.renderPropertiesTable(lines, entity.properties);
209
+ }
210
+ lines.push('---');
211
+ lines.push('');
212
+ }
213
+ renderPropertiesTable(lines, properties) {
214
+ lines.push('| Property | Type | Description |');
215
+ lines.push('|----------|------|-------------|');
216
+ for (const prop of [...properties].sort((a, b) => a.name.localeCompare(b.name))) {
217
+ const rangeLinks = prop.rangeTypes.map((r) => {
218
+ const link = this.renderInternalLink(r);
219
+ return prop.isObjectProperty ? `-> ${link}` : link;
220
+ });
221
+ let typeStr = rangeLinks.join(', ');
222
+ if (prop.isCollection) {
223
+ typeStr = `${typeStr}[]`;
224
+ }
225
+ const desc = (prop.comment ?? '')
226
+ .replace(/\|/g, '\\|')
227
+ .replace(/\n/g, ' ')
228
+ .replace(/\r/g, '');
229
+ lines.push(`| ${prop.name} | ${typeStr} | ${desc} |`);
230
+ }
231
+ lines.push('');
232
+ }
233
+ renderUnresolved(lines, unresolved) {
234
+ lines.push(`## Unresolved References (${unresolved.length})`);
235
+ lines.push('');
236
+ lines.push('*The following references could not be resolved:*');
237
+ lines.push('');
238
+ for (const entity of unresolved) {
239
+ lines.push(`- \`${entity.reference}\`: ${entity.reason}`);
240
+ }
241
+ lines.push('');
242
+ }
243
+ }
244
+ function formatEntityType(type) {
245
+ switch (type) {
246
+ case ResolvedResourceType.Class: return 'Class';
247
+ case ResolvedResourceType.AnnotationProperty: return 'Annotation Property';
248
+ case ResolvedResourceType.DatatypeProperty: return 'Datatype Property';
249
+ case ResolvedResourceType.ObjectProperty: return 'Object Property';
250
+ case ResolvedResourceType.Instance: return 'Instance';
251
+ default: return 'Other';
252
+ }
253
+ }
254
+ function truncateForTable(text) {
255
+ if (!text)
256
+ return '';
257
+ text = text.replace(/\|/g, '\\|').replace(/\n/g, ' ').replace(/\r/g, '');
258
+ return text.length <= 50 ? text : text.substring(0, 47) + '...';
259
+ }
260
+ function makeMermaidSafeId(name) {
261
+ return name.replace(/[^a-zA-Z0-9]/g, '_');
262
+ }