@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,205 @@
1
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
2
+ import { ResourceResolver } from '../../../resolution/ResourceResolver.js';
3
+ export class ClassDefinitionRule {
4
+ builtInClasses = new Set([
5
+ 'Resource',
6
+ 'Class',
7
+ 'Literal',
8
+ 'Thing',
9
+ 'Nothing',
10
+ 'Property',
11
+ 'DatatypeProperty',
12
+ 'ObjectProperty',
13
+ 'AnnotationProperty',
14
+ 'FunctionalProperty',
15
+ 'InverseFunctionalProperty',
16
+ 'TransitiveProperty',
17
+ 'SymmetricProperty'
18
+ ]);
19
+ get ruleName() {
20
+ return 'ClassDefinition';
21
+ }
22
+ async validateAsync(document, repository) {
23
+ const errors = [];
24
+ const usedClasses = new Map(); // class -> entities using it
25
+ const definedClasses = new Set();
26
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
27
+ if (typeof entityValue === 'object' && entityValue !== null && !Array.isArray(entityValue)) {
28
+ const entityDict = entityValue;
29
+ const typeObj = entityDict['type'];
30
+ if (typeObj) {
31
+ if (Array.isArray(typeObj)) {
32
+ for (const type of typeObj) {
33
+ const typeStr = type?.toString();
34
+ if (typeStr === 'Class' || (typeStr && typeStr.endsWith('.Class'))) {
35
+ definedClasses.add(entityName);
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ else {
41
+ const typeStr = typeObj?.toString();
42
+ if (typeStr === 'Class' || (typeStr && typeStr.endsWith('.Class'))) {
43
+ definedClasses.add(entityName);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
50
+ if (typeof entityValue === 'object' && entityValue !== null && !Array.isArray(entityValue)) {
51
+ const entityDict = entityValue;
52
+ const typeObj = entityDict['type'];
53
+ if (typeObj) {
54
+ if (Array.isArray(typeObj)) {
55
+ let shouldSkip = false;
56
+ for (const type of typeObj) {
57
+ const typeStr = type?.toString();
58
+ if (typeStr) {
59
+ if (this.isDefinitionType(typeStr)) {
60
+ shouldSkip = true;
61
+ break;
62
+ }
63
+ }
64
+ }
65
+ if (!shouldSkip) {
66
+ for (const type of typeObj) {
67
+ const typeStr = type?.toString();
68
+ if (typeStr && typeStr.trim().length > 0) {
69
+ if (!usedClasses.has(typeStr)) {
70
+ usedClasses.set(typeStr, []);
71
+ }
72
+ usedClasses.get(typeStr).push(entityName);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ else {
78
+ const typeStr = typeObj?.toString();
79
+ if (typeStr && typeStr.trim().length > 0) {
80
+ if (!this.isDefinitionType(typeStr)) {
81
+ if (!usedClasses.has(typeStr)) {
82
+ usedClasses.set(typeStr, []);
83
+ }
84
+ usedClasses.get(typeStr).push(entityName);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ this.checkNestedTypes(entityDict, entityName, usedClasses, entityName);
90
+ }
91
+ }
92
+ const resolver = new ResourceResolver(repository);
93
+ for (const [className, entities] of usedClasses) {
94
+ if (this.builtInClasses.has(className)) {
95
+ continue;
96
+ }
97
+ if (definedClasses.has(className)) {
98
+ continue;
99
+ }
100
+ const resolvedEntity = await resolver.resolveEntityAsync(className, document);
101
+ const isAvailable = resolvedEntity && this.isClassEntity(resolvedEntity.entity);
102
+ if (!isAvailable) {
103
+ const uniqueEntities = Array.from(new Set(entities)).sort();
104
+ for (const entityName of uniqueEntities) {
105
+ errors.push({
106
+ ruleType: this.ruleName,
107
+ severity: ValidationSeverity.Error,
108
+ message: `Class '${className}' is not defined or imported`,
109
+ suggestion: `Define '${className}' as a Class in this document, or import a namespace that contains it`,
110
+ entityName: entityName, // Actual entity name for position finding
111
+ propertyName: 'type', // The property with the invalid value
112
+ actualValue: className,
113
+ expectedValue: 'Defined or imported class'
114
+ });
115
+ }
116
+ }
117
+ }
118
+ return errors;
119
+ }
120
+ checkNestedTypes(value, parentEntity, usedClasses, currentPath) {
121
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
122
+ const dict = value;
123
+ const typeObj = dict['type'];
124
+ if (typeObj) {
125
+ const skipTypes = new Set(['Class', 'DatatypeProperty', 'ObjectProperty', 'AnnotationProperty']);
126
+ if (Array.isArray(typeObj)) {
127
+ for (const type of typeObj) {
128
+ const typeStr = type?.toString();
129
+ if (typeStr && typeStr.trim().length > 0 && !skipTypes.has(typeStr)) {
130
+ if (!usedClasses.has(typeStr)) {
131
+ usedClasses.set(typeStr, []);
132
+ }
133
+ usedClasses.get(typeStr).push(currentPath);
134
+ }
135
+ }
136
+ }
137
+ else {
138
+ const typeStr = typeObj?.toString();
139
+ if (typeStr && typeStr.trim().length > 0 && !skipTypes.has(typeStr)) {
140
+ if (!usedClasses.has(typeStr)) {
141
+ usedClasses.set(typeStr, []);
142
+ }
143
+ usedClasses.get(typeStr).push(currentPath);
144
+ }
145
+ }
146
+ }
147
+ for (const [key, val] of Object.entries(dict)) {
148
+ if (key !== 'type') {
149
+ const newPath = `${currentPath}.${key}`;
150
+ this.checkNestedTypes(val, parentEntity, usedClasses, newPath);
151
+ }
152
+ }
153
+ }
154
+ else if (Array.isArray(value)) {
155
+ for (let i = 0; i < value.length; i++) {
156
+ const newPath = `${currentPath}[${i}]`;
157
+ this.checkNestedTypes(value[i], parentEntity, usedClasses, newPath);
158
+ }
159
+ }
160
+ }
161
+ isClassEntity(entityDict) {
162
+ if (!entityDict) {
163
+ return false;
164
+ }
165
+ const typeObj = entityDict['type'];
166
+ if (!typeObj) {
167
+ return false;
168
+ }
169
+ if (Array.isArray(typeObj)) {
170
+ for (const type of typeObj) {
171
+ const typeStr = type?.toString();
172
+ if (typeStr === 'Class' || (typeStr && typeStr.endsWith('.Class'))) {
173
+ return true;
174
+ }
175
+ }
176
+ }
177
+ else {
178
+ const typeStr = typeObj?.toString();
179
+ if (typeStr === 'Class' || (typeStr && typeStr.endsWith('.Class'))) {
180
+ return true;
181
+ }
182
+ }
183
+ return false;
184
+ }
185
+ isDefinitionType(typeStr) {
186
+ if (!typeStr || typeStr.trim().length === 0) {
187
+ return false;
188
+ }
189
+ const definitionTypes = new Set([
190
+ 'Class',
191
+ 'Property',
192
+ 'DatatypeProperty',
193
+ 'ObjectProperty',
194
+ 'AnnotationProperty'
195
+ ]);
196
+ if (definitionTypes.has(typeStr)) {
197
+ return true;
198
+ }
199
+ return (typeStr.endsWith('.Class') ||
200
+ typeStr.endsWith('.Property') ||
201
+ typeStr.endsWith('.DatatypeProperty') ||
202
+ typeStr.endsWith('.ObjectProperty') ||
203
+ typeStr.endsWith('.AnnotationProperty'));
204
+ }
205
+ }
@@ -0,0 +1,11 @@
1
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
2
+ import type { CanonDocument } from '@canon-protocol/types/document/models/types';
3
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
4
+ import type { IRepositoryValidationRule } from './IRepositoryValidationRule.js';
5
+ export declare class ClassHierarchyCycleRule implements IRepositoryValidationRule {
6
+ readonly ruleName = "ClassHierarchyCycle";
7
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
8
+ private buildClassHierarchy;
9
+ private detectCycle;
10
+ private detectCycleRecursive;
11
+ }
@@ -0,0 +1,98 @@
1
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
2
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
3
+ import { normalizeToStringList } from '../normalizeToStringList.js';
4
+ export class ClassHierarchyCycleRule {
5
+ ruleName = 'ClassHierarchyCycle';
6
+ async validateAsync(document, repository) {
7
+ const errors = [];
8
+ const classHierarchy = new Map();
9
+ const visitedDocs = new Set();
10
+ await this.buildClassHierarchy(document, repository, classHierarchy, visitedDocs);
11
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
12
+ if (entityValue && typeof entityValue === 'object' && !Array.isArray(entityValue)) {
13
+ const entityDict = entityValue;
14
+ const typeValue = entityDict['type'];
15
+ const subClassOfValue = entityDict['subClassOf'];
16
+ if (typeValue?.toString() === 'Class' && subClassOfValue) {
17
+ const className = entityName;
18
+ const cycle = this.detectCycle(className, classHierarchy);
19
+ if (cycle) {
20
+ const cycleChain = cycle.join(' → ');
21
+ const error = new OntologyValidationError();
22
+ error.ruleType = this.ruleName;
23
+ error.severity = ValidationSeverity.Error;
24
+ error.message = `Circular class hierarchy detected: ${cycleChain}`;
25
+ error.suggestion = 'Remove the circular dependency by breaking one of the subClassOf relationships';
26
+ error.entityName = className;
27
+ error.propertyName = 'subClassOf';
28
+ error.actualValue = cycleChain;
29
+ errors.push(error);
30
+ break;
31
+ }
32
+ }
33
+ }
34
+ }
35
+ return errors;
36
+ }
37
+ async buildClassHierarchy(document, repository, hierarchy, visitedDocs) {
38
+ const docId = document.metadata?.namespace_?.toString() ?? '';
39
+ if (docId && visitedDocs.has(docId)) {
40
+ return; // Already processed this document
41
+ }
42
+ if (docId) {
43
+ visitedDocs.add(docId);
44
+ }
45
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
46
+ if (entityValue && typeof entityValue === 'object' && !Array.isArray(entityValue)) {
47
+ const entityDict = entityValue;
48
+ const typeValue = entityDict['type'];
49
+ const subClassOfValue = entityDict['subClassOf'];
50
+ if (typeValue?.toString() === 'Class' && subClassOfValue) {
51
+ const parentClasses = normalizeToStringList(subClassOfValue);
52
+ if (parentClasses.length > 0) {
53
+ hierarchy.set(entityName, parentClasses);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ if (document.metadata?.imports) {
59
+ for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
60
+ for (const import_ of imports) {
61
+ const importedDoc = await repository.getHighestCompatibleVersionAsync(publisher, import_);
62
+ if (importedDoc) {
63
+ await this.buildClassHierarchy(importedDoc, repository, hierarchy, visitedDocs);
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ detectCycle(startClass, hierarchy) {
70
+ const path = [];
71
+ const visited = new Set();
72
+ return this.detectCycleRecursive(startClass, hierarchy, path, visited);
73
+ }
74
+ detectCycleRecursive(className, hierarchy, path, visited) {
75
+ if (path.includes(className)) {
76
+ const cycleStart = path.indexOf(className);
77
+ const cycle = path.slice(cycleStart);
78
+ cycle.push(className); // Complete the cycle
79
+ return cycle;
80
+ }
81
+ if (visited.has(className)) {
82
+ return null;
83
+ }
84
+ path.push(className);
85
+ visited.add(className);
86
+ const parentClasses = hierarchy.get(className);
87
+ if (parentClasses) {
88
+ for (const parentClass of parentClasses) {
89
+ const cycle = this.detectCycleRecursive(parentClass, hierarchy, path, visited);
90
+ if (cycle) {
91
+ return cycle;
92
+ }
93
+ }
94
+ }
95
+ path.pop();
96
+ return null;
97
+ }
98
+ }
@@ -0,0 +1,13 @@
1
+ import type { CanonDocument } from '@canon-protocol/types/document/models/types';
2
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
3
+ import type { IRepositoryValidationRule } from './IRepositoryValidationRule.js';
4
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
5
+ export declare class DefinitionPropertyReferenceRule implements IRepositoryValidationRule {
6
+ private readonly metadataKeys;
7
+ get ruleName(): string;
8
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
9
+ private isPropertyType;
10
+ private isClassType;
11
+ private isPropertyAvailableInImports;
12
+ private isPropertyAvailableInImportsRecursive;
13
+ }
@@ -0,0 +1,162 @@
1
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
2
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
3
+ export class DefinitionPropertyReferenceRule {
4
+ metadataKeys = new Set([
5
+ 'type',
6
+ 'label',
7
+ 'comment',
8
+ 'subClassOf',
9
+ 'subPropertyOf',
10
+ 'domain',
11
+ 'range',
12
+ 'inverseOf',
13
+ 'transitive',
14
+ 'symmetric',
15
+ 'functional',
16
+ 'inverseFunctional'
17
+ ]);
18
+ get ruleName() {
19
+ return 'DefinitionPropertyReference';
20
+ }
21
+ async validateAsync(document, repository) {
22
+ const errors = [];
23
+ const definedProperties = new Set();
24
+ for (const [entityName, entityDef] of Object.entries(document.body)) {
25
+ if (typeof entityDef === 'object' && entityDef !== null && !Array.isArray(entityDef)) {
26
+ const entity = entityDef;
27
+ const type = entity['type']?.toString();
28
+ if (this.isPropertyType(type)) {
29
+ definedProperties.add(entityName);
30
+ }
31
+ }
32
+ }
33
+ for (const [entityName, entityDef] of Object.entries(document.body)) {
34
+ if (typeof entityDef === 'object' && entityDef !== null && !Array.isArray(entityDef)) {
35
+ const entity = entityDef;
36
+ const type = entity['type']?.toString();
37
+ if (!this.isPropertyType(type) && !this.isClassType(type)) {
38
+ continue;
39
+ }
40
+ for (const [propertyName, propertyValue] of Object.entries(entity)) {
41
+ if (!propertyName) {
42
+ continue;
43
+ }
44
+ if (this.metadataKeys.has(propertyName)) {
45
+ continue;
46
+ }
47
+ let actualPropertyName = propertyName;
48
+ let namespaceAlias = null;
49
+ if (propertyName.includes('.')) {
50
+ const lastDotIndex = propertyName.lastIndexOf('.');
51
+ namespaceAlias = propertyName.substring(0, lastDotIndex);
52
+ actualPropertyName = propertyName.substring(lastDotIndex + 1);
53
+ }
54
+ if (definedProperties.has(actualPropertyName)) {
55
+ continue;
56
+ }
57
+ const isAvailable = await this.isPropertyAvailableInImports(document, repository, actualPropertyName, namespaceAlias);
58
+ if (!isAvailable) {
59
+ const definitionType = this.isClassType(type) ? 'class' : 'property';
60
+ const error = new OntologyValidationError();
61
+ error.ruleType = this.ruleName;
62
+ error.severity = ValidationSeverity.Error;
63
+ error.message = `Property '${propertyName}' used on ${definitionType} '${entityName}' is not defined or imported`;
64
+ error.suggestion = namespaceAlias
65
+ ? `The property '${actualPropertyName}' is not found in the imported namespace with alias '${namespaceAlias}'.\n` +
66
+ ` 1. Verify the namespace is imported with the correct alias\n` +
67
+ ` 2. Check if the property name is spelled correctly\n` +
68
+ ` 3. Ensure the imported namespace version contains this property`
69
+ : `The property '${propertyName}' is not defined in this namespace or any imported namespace.\n` +
70
+ ` 1. Define '${propertyName}' as an ObjectProperty or DatatypeProperty in this namespace, OR\n` +
71
+ ` 2. Import the namespace that contains '${propertyName}', OR\n` +
72
+ ` 3. Use a qualified reference like 'alias.${propertyName}' if already imported with an alias`;
73
+ error.entityName = entityName;
74
+ error.propertyName = propertyName;
75
+ error.actualValue = propertyValue?.toString();
76
+ error.expectedValue = 'A defined or imported property';
77
+ errors.push(error);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ return errors;
83
+ }
84
+ isPropertyType(typeStr) {
85
+ if (!typeStr) {
86
+ return false;
87
+ }
88
+ return (typeStr === 'ObjectProperty' ||
89
+ typeStr === 'DatatypeProperty' ||
90
+ typeStr === 'AnnotationProperty' ||
91
+ typeStr === 'Property' ||
92
+ typeStr.endsWith('.ObjectProperty') ||
93
+ typeStr.endsWith('.DatatypeProperty') ||
94
+ typeStr.endsWith('.AnnotationProperty') ||
95
+ typeStr.endsWith('.Property'));
96
+ }
97
+ isClassType(typeStr) {
98
+ if (!typeStr) {
99
+ return false;
100
+ }
101
+ return typeStr === 'Class' || typeStr.endsWith('.Class');
102
+ }
103
+ async isPropertyAvailableInImports(document, repository, propertyName, namespaceAlias) {
104
+ if (!document.metadata.imports) {
105
+ return false;
106
+ }
107
+ for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
108
+ for (const import_ of imports) {
109
+ if (namespaceAlias !== null && import_.alias !== namespaceAlias) {
110
+ continue;
111
+ }
112
+ const importedDoc = await repository.getHighestCompatibleVersionAsync(publisher, import_);
113
+ if (importedDoc) {
114
+ const entityDef = importedDoc.body[propertyName];
115
+ if (entityDef && typeof entityDef === 'object' && !Array.isArray(entityDef)) {
116
+ const entity = entityDef;
117
+ const type = entity['type']?.toString();
118
+ if (this.isPropertyType(type)) {
119
+ return true;
120
+ }
121
+ }
122
+ const visitedDocs = new Set();
123
+ if (await this.isPropertyAvailableInImportsRecursive(importedDoc, repository, propertyName, visitedDocs)) {
124
+ return true;
125
+ }
126
+ }
127
+ }
128
+ }
129
+ return false;
130
+ }
131
+ async isPropertyAvailableInImportsRecursive(document, repository, propertyName, visitedDocs) {
132
+ const docId = document.metadata.namespace_?.toString() ?? '';
133
+ if (docId && visitedDocs.has(docId)) {
134
+ return false;
135
+ }
136
+ if (docId) {
137
+ visitedDocs.add(docId);
138
+ }
139
+ if (!document.metadata.imports) {
140
+ return false;
141
+ }
142
+ for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
143
+ for (const import_ of imports) {
144
+ const importedDoc = await repository.getHighestCompatibleVersionAsync(publisher, import_);
145
+ if (importedDoc) {
146
+ const entityDef = importedDoc.body[propertyName];
147
+ if (entityDef && typeof entityDef === 'object' && !Array.isArray(entityDef)) {
148
+ const entity = entityDef;
149
+ const type = entity['type']?.toString();
150
+ if (this.isPropertyType(type)) {
151
+ return true;
152
+ }
153
+ }
154
+ if (await this.isPropertyAvailableInImportsRecursive(importedDoc, repository, propertyName, visitedDocs)) {
155
+ return true;
156
+ }
157
+ }
158
+ }
159
+ }
160
+ return false;
161
+ }
162
+ }
@@ -0,0 +1,7 @@
1
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
2
+ import type { CanonDocument } from '@canon-protocol/types/document/models/types';
3
+ import type { OntologyValidationError } from '../../OntologyValidationError.js';
4
+ export interface IRepositoryValidationRule {
5
+ readonly ruleName: string;
6
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
7
+ }
@@ -0,0 +1,12 @@
1
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
2
+ import type { CanonDocument } from '@canon-protocol/types/document/models/types';
3
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
4
+ import type { IRepositoryValidationRule } from './IRepositoryValidationRule.js';
5
+ export declare class ImportExistenceRule implements IRepositoryValidationRule {
6
+ readonly ruleName = "ImportExistence";
7
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
8
+ private determineImportErrorAsync;
9
+ private getAcceptableVersionRange;
10
+ private findSimilarPackages;
11
+ private levenshteinDistance;
12
+ }
@@ -0,0 +1,124 @@
1
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
2
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
3
+ export class ImportExistenceRule {
4
+ ruleName = 'ImportExistence';
5
+ async validateAsync(document, repository) {
6
+ const errors = [];
7
+ if (!document.metadata?.imports) {
8
+ return errors; // No imports to validate
9
+ }
10
+ const allDocuments = await repository.getAllDocumentsAsync();
11
+ for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
12
+ for (const import_ of imports) {
13
+ const resolvedDocument = await repository.getHighestCompatibleVersionAsync(publisher, import_);
14
+ if (!resolvedDocument) {
15
+ const errorDetails = await this.determineImportErrorAsync(repository, publisher, import_, allDocuments);
16
+ const error = new OntologyValidationError();
17
+ error.ruleType = this.ruleName;
18
+ error.severity = ValidationSeverity.Error;
19
+ error.message = errorDetails.message;
20
+ error.suggestion = errorDetails.suggestion;
21
+ error.entityName = `Import: ${publisher}/${import_.packageName}`;
22
+ error.actualValue = import_.toString();
23
+ error.expectedValue = errorDetails.expectedValue;
24
+ errors.push(error);
25
+ }
26
+ }
27
+ }
28
+ return errors;
29
+ }
30
+ async determineImportErrorAsync(repository, publisher, import_, allDocuments) {
31
+ const allVersions = await repository.getDocumentsByNamespaceAsync(publisher, import_.packageName);
32
+ if (allVersions.length === 0) {
33
+ const availablePackages = allDocuments
34
+ .filter(d => d.metadata.namespace_?.publisher === publisher)
35
+ .map(d => d.metadata.namespace_.package_)
36
+ .filter((pkg, index, self) => self.indexOf(pkg) === index); // Distinct
37
+ const suggestions = this.findSimilarPackages(import_.packageName, availablePackages);
38
+ const message = `Import '${publisher}/${import_.package_}' not found - package does not exist`;
39
+ const suggestion = suggestions.length > 0
40
+ ? `Did you mean: ${suggestions.slice(0, 3).join(', ')}?`
41
+ : availablePackages.length > 0
42
+ ? `Available packages in ${publisher}: ${availablePackages.slice(0, 5).join(', ')}`
43
+ : `No packages found for publisher '${publisher}'. Verify the publisher name is correct.`;
44
+ return {
45
+ message,
46
+ suggestion,
47
+ expectedValue: 'Existing package'
48
+ };
49
+ }
50
+ const availableVersions = allVersions
51
+ .filter(d => d.metadata.namespace_?.version)
52
+ .map(d => d.metadata.namespace_.version.toString())
53
+ .sort();
54
+ const versionList = availableVersions.join(', ');
55
+ const acceptableRange = this.getAcceptableVersionRange(import_);
56
+ return {
57
+ message: `Import '${publisher}/${import_.toString()}' version mismatch - no compatible version found`,
58
+ suggestion: `Available versions: ${versionList}. Required: ${acceptableRange}`,
59
+ expectedValue: acceptableRange
60
+ };
61
+ }
62
+ getAcceptableVersionRange(import_) {
63
+ switch (import_.versionOperator) {
64
+ case 0: // Exact
65
+ return `exactly ${import_.version.major}.${import_.version.minor}.${import_.version.patch}`;
66
+ case 1: // Compatible (~)
67
+ return `${import_.version.major}.${import_.version.minor}.${import_.version.patch} to ${import_.version.major}.${import_.version.minor}.x`;
68
+ case 2: // Major (^)
69
+ return import_.version.major === 0
70
+ ? `0.${import_.version.minor}.x`
71
+ : `${import_.version.major}.x.x`;
72
+ case 3: // Any (>=)
73
+ return `any version >= ${import_.version.major}.${import_.version.minor}.${import_.version.patch}`;
74
+ default:
75
+ return import_.toString();
76
+ }
77
+ }
78
+ findSimilarPackages(targetPackage, availablePackages) {
79
+ if (availablePackages.length === 0) {
80
+ return [];
81
+ }
82
+ const substringMatches = availablePackages.filter(pkg => pkg.toLowerCase().includes(targetPackage.toLowerCase()) ||
83
+ targetPackage.toLowerCase().includes(pkg.toLowerCase()));
84
+ if (substringMatches.length > 0) {
85
+ return substringMatches.sort((a, b) => a.length - b.length); // Prefer shorter matches
86
+ }
87
+ const scoredPackages = availablePackages
88
+ .map(pkg => ({
89
+ package: pkg,
90
+ distance: this.levenshteinDistance(targetPackage.toLowerCase(), pkg.toLowerCase())
91
+ }))
92
+ .sort((a, b) => a.distance - b.distance);
93
+ const threshold = Math.max(3, Math.floor(targetPackage.length * 0.4));
94
+ return scoredPackages
95
+ .filter(x => x.distance <= threshold)
96
+ .map(x => x.package);
97
+ }
98
+ levenshteinDistance(source, target) {
99
+ if (!source || source.length === 0) {
100
+ return target?.length ?? 0;
101
+ }
102
+ if (!target || target.length === 0) {
103
+ return source.length;
104
+ }
105
+ const sourceLength = source.length;
106
+ const targetLength = target.length;
107
+ const distance = Array(sourceLength + 1)
108
+ .fill(null)
109
+ .map(() => Array(targetLength + 1).fill(0));
110
+ for (let i = 0; i <= sourceLength; i++) {
111
+ distance[i][0] = i;
112
+ }
113
+ for (let j = 0; j <= targetLength; j++) {
114
+ distance[0][j] = j;
115
+ }
116
+ for (let i = 1; i <= sourceLength; i++) {
117
+ for (let j = 1; j <= targetLength; j++) {
118
+ const cost = source[i - 1] === target[j - 1] ? 0 : 1;
119
+ distance[i][j] = Math.min(Math.min(distance[i - 1][j] + 1, distance[i][j - 1] + 1), distance[i - 1][j - 1] + cost);
120
+ }
121
+ }
122
+ return distance[sourceLength][targetLength];
123
+ }
124
+ }
@@ -0,0 +1,13 @@
1
+ import type { CanonDocument } from '@canon-protocol/types/document/models/types';
2
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
3
+ import type { IRepositoryValidationRule } from './IRepositoryValidationRule.js';
4
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
5
+ export declare class InstancePropertyReferenceRule implements IRepositoryValidationRule {
6
+ private readonly metadataKeys;
7
+ get ruleName(): string;
8
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
9
+ private isPropertyType;
10
+ private isClassType;
11
+ private isPropertyAvailableInImports;
12
+ private isPropertyAvailableInImportsRecursive;
13
+ }