@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,281 @@
1
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
2
+ import { ResourceResolver } from '../../../resolution/ResourceResolver.js';
3
+ import { TypeResolver } from '../../../resolution/TypeResolver.js';
4
+ export class ObjectPropertyValueValidationRule {
5
+ standardProperties = new Set([
6
+ 'type', // Special meta-property that references class definitions (validated separately)
7
+ 'label',
8
+ 'comment',
9
+ 'subClassOf',
10
+ 'domain',
11
+ 'range',
12
+ 'subPropertyOf',
13
+ 'inverseOf',
14
+ 'sameAs',
15
+ 'seeAlso',
16
+ 'isDefinedBy'
17
+ ]);
18
+ get ruleName() {
19
+ return 'ObjectPropertyValue';
20
+ }
21
+ async validateAsync(document, repository) {
22
+ const errors = [];
23
+ const resourceResolver = new ResourceResolver(repository);
24
+ const typeResolver = new TypeResolver(resourceResolver);
25
+ const entityIndex = await resourceResolver.buildEntityIndexAsync(document);
26
+ const propertyMetadata = await this.buildPropertyMetadataAsync(document, repository, resourceResolver, entityIndex);
27
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
28
+ if (typeof entityValue === 'object' && entityValue !== null && !Array.isArray(entityValue)) {
29
+ await this.scanEntityForObjectProperties(entityValue, entityName, '', // Start with empty path - entity name is tracked separately in EntityName
30
+ propertyMetadata, resourceResolver, typeResolver, document, errors);
31
+ }
32
+ }
33
+ return errors;
34
+ }
35
+ async scanEntityForObjectProperties(entityDict, rootEntityName, currentPath, propertyMetadata, resourceResolver, typeResolver, document, errors) {
36
+ for (const [propertyName, value] of Object.entries(entityDict)) {
37
+ if (this.standardProperties.has(propertyName)) {
38
+ continue;
39
+ }
40
+ let lookupName = propertyName;
41
+ const dotIndex = propertyName.lastIndexOf('.');
42
+ if (dotIndex > 0 && dotIndex < propertyName.length - 1) {
43
+ lookupName = propertyName.substring(dotIndex + 1);
44
+ }
45
+ const propInfo = propertyMetadata.get(lookupName);
46
+ if (propInfo) {
47
+ const isObjectProperty = typeResolver.isEffectiveObjectProperty(propInfo.propertyType, propInfo.rangeUri);
48
+ if (isObjectProperty) {
49
+ const fullPath = currentPath ? `${currentPath}.${propertyName}` : propertyName;
50
+ if (typeof value === 'string' && value.trim().length > 0) {
51
+ const strValue = value.trim();
52
+ if (strValue.includes(',')) {
53
+ const parts = strValue.split(',').map(s => s.trim()).filter(s => s.length > 0);
54
+ errors.push({
55
+ ruleType: this.ruleName,
56
+ severity: ValidationSeverity.Error,
57
+ message: `Property '${propertyName}' has a comma-separated string value, but ObjectProperty expects a list of references`,
58
+ suggestion: `Use YAML list format:\n ${propertyName}:\n - ${parts[0]}\n${parts.slice(1).map(p => ` - ${p}`).join('\n')}`,
59
+ entityName: rootEntityName,
60
+ propertyName: propertyName,
61
+ propertyPath: fullPath,
62
+ actualValue: strValue,
63
+ expectedValue: `A single reference to ${propInfo.range ?? 'an entity'} OR a YAML list`
64
+ });
65
+ continue; // Skip further validation for this malformed value
66
+ }
67
+ const resolution = await resourceResolver.resolveEntityAsync(strValue, document);
68
+ if (!resolution) {
69
+ const rangeHint = propInfo.range ?? 'entity';
70
+ errors.push({
71
+ ruleType: this.ruleName,
72
+ severity: ValidationSeverity.Error,
73
+ message: `Property '${propertyName}' references '${strValue}' which is not defined or imported`,
74
+ suggestion: `Define '${strValue}' or import a namespace that contains it. Expected range: ${rangeHint}`,
75
+ entityName: rootEntityName,
76
+ propertyName: propertyName,
77
+ propertyPath: fullPath,
78
+ actualValue: strValue,
79
+ expectedValue: 'Defined or imported entity'
80
+ });
81
+ }
82
+ else if (propInfo.range) {
83
+ const isCorrectType = await this.validateRangeType(resolution, propInfo.range, resourceResolver, document);
84
+ if (!isCorrectType) {
85
+ errors.push({
86
+ ruleType: this.ruleName,
87
+ severity: ValidationSeverity.Warning,
88
+ message: `Property '${propertyName}' expects a ${propInfo.range}, but '${strValue}' may not be a ${propInfo.range}`,
89
+ suggestion: `Ensure '${strValue}' is defined with appropriate type`,
90
+ entityName: rootEntityName,
91
+ propertyName: propertyName,
92
+ propertyPath: fullPath,
93
+ actualValue: strValue,
94
+ expectedValue: propInfo.range
95
+ });
96
+ }
97
+ }
98
+ }
99
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
100
+ const newPath = currentPath ? `${currentPath}.${propertyName}` : propertyName;
101
+ await this.scanEntityForObjectProperties(value, rootEntityName, newPath, propertyMetadata, resourceResolver, typeResolver, document, errors);
102
+ }
103
+ else if (Array.isArray(value)) {
104
+ for (let i = 0; i < value.length; i++) {
105
+ const item = value[i];
106
+ if (typeof item === 'string' && item.trim().length > 0) {
107
+ const strItem = item.trim();
108
+ const resolution = await resourceResolver.resolveEntityAsync(strItem, document);
109
+ if (!resolution) {
110
+ const rangeHint = propInfo.range ?? 'entity';
111
+ const arrayPath = currentPath
112
+ ? `${currentPath}.${propertyName}[${i}]`
113
+ : `${propertyName}[${i}]`;
114
+ errors.push({
115
+ ruleType: this.ruleName,
116
+ severity: ValidationSeverity.Error,
117
+ message: `Property '${propertyName}' references '${strItem}' which is not defined or imported`,
118
+ suggestion: `Define '${strItem}' or import a namespace that contains it. Expected range: ${rangeHint}`,
119
+ entityName: rootEntityName,
120
+ propertyName: `${propertyName}[${i}]`,
121
+ propertyPath: arrayPath,
122
+ actualValue: strItem,
123
+ expectedValue: 'Defined or imported entity'
124
+ });
125
+ }
126
+ else if (propInfo.range) {
127
+ const isCorrectType = await this.validateRangeType(resolution, propInfo.range, resourceResolver, document);
128
+ if (!isCorrectType) {
129
+ const arrayPath = currentPath
130
+ ? `${currentPath}.${propertyName}[${i}]`
131
+ : `${propertyName}[${i}]`;
132
+ errors.push({
133
+ ruleType: this.ruleName,
134
+ severity: ValidationSeverity.Warning,
135
+ message: `Property '${propertyName}' expects a ${propInfo.range}, but '${strItem}' may not be a ${propInfo.range}`,
136
+ suggestion: `Ensure '${strItem}' is defined with appropriate type`,
137
+ entityName: rootEntityName,
138
+ propertyName: `${propertyName}[${i}]`,
139
+ propertyPath: arrayPath,
140
+ actualValue: strItem,
141
+ expectedValue: propInfo.range
142
+ });
143
+ }
144
+ }
145
+ }
146
+ else if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
147
+ const newPath = currentPath
148
+ ? `${currentPath}.${propertyName}[${i}]`
149
+ : `${propertyName}[${i}]`;
150
+ await this.scanEntityForObjectProperties(item, rootEntityName, newPath, propertyMetadata, resourceResolver, typeResolver, document, errors);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ if (typeof value === 'object' &&
157
+ value !== null &&
158
+ !Array.isArray(value) &&
159
+ !propertyMetadata.has(propertyName)) {
160
+ const newPath = currentPath ? `${currentPath}.${propertyName}` : propertyName;
161
+ await this.scanEntityForObjectProperties(value, rootEntityName, newPath, propertyMetadata, resourceResolver, typeResolver, document, errors);
162
+ }
163
+ else if (Array.isArray(value)) {
164
+ for (let i = 0; i < value.length; i++) {
165
+ const item = value[i];
166
+ if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
167
+ const newPath = currentPath
168
+ ? `${currentPath}.${propertyName}[${i}]`
169
+ : `${propertyName}[${i}]`;
170
+ await this.scanEntityForObjectProperties(item, rootEntityName, newPath, propertyMetadata, resourceResolver, typeResolver, document, errors);
171
+ }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ async validateRangeType(resolution, expectedRange, resourceResolver, document) {
177
+ const metaTypes = new Set([
178
+ 'Property',
179
+ 'DatatypeProperty',
180
+ 'ObjectProperty',
181
+ 'AnnotationProperty',
182
+ 'Class',
183
+ 'Resource'
184
+ ]);
185
+ if (!metaTypes.has(expectedRange)) {
186
+ return true; // Don't validate non-meta-types
187
+ }
188
+ const typeValue = resolution.entity['type'];
189
+ if (typeValue) {
190
+ const types = [];
191
+ if (Array.isArray(typeValue)) {
192
+ for (const t of typeValue) {
193
+ const typeStr = t?.toString();
194
+ if (typeStr && typeStr.trim().length > 0) {
195
+ types.push(typeStr);
196
+ }
197
+ }
198
+ }
199
+ else {
200
+ const typeStr = typeValue?.toString();
201
+ if (typeStr && typeStr.trim().length > 0) {
202
+ types.push(typeStr);
203
+ }
204
+ }
205
+ for (const type of types) {
206
+ const typeName = type.includes('.') ? type.split('.')[1] : type;
207
+ if (typeName === expectedRange) {
208
+ return true;
209
+ }
210
+ if (expectedRange === 'Property') {
211
+ if (typeName === 'DatatypeProperty' ||
212
+ typeName === 'ObjectProperty' ||
213
+ typeName === 'AnnotationProperty') {
214
+ return true;
215
+ }
216
+ }
217
+ if (expectedRange === 'Class') {
218
+ if (typeName === 'Class') {
219
+ return true;
220
+ }
221
+ }
222
+ if (await resourceResolver.isSubclassOfAsync(type, expectedRange, document)) {
223
+ return true;
224
+ }
225
+ }
226
+ }
227
+ return false;
228
+ }
229
+ async buildPropertyMetadataAsync(document, _repository, resourceResolver, entityIndex) {
230
+ const metadata = new Map();
231
+ for (const [entityName, resolution] of entityIndex) {
232
+ const typeValue = resolution.entity['type'];
233
+ if (typeValue) {
234
+ const types = [];
235
+ if (Array.isArray(typeValue)) {
236
+ for (const t of typeValue) {
237
+ const typeStr = t?.toString();
238
+ if (typeStr && typeStr.trim().length > 0) {
239
+ types.push(typeStr);
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ const typeStr = typeValue?.toString();
245
+ if (typeStr && typeStr.trim().length > 0) {
246
+ types.push(typeStr);
247
+ }
248
+ }
249
+ let isProperty = false;
250
+ let propertyType = 'Property';
251
+ for (const type of types) {
252
+ const typeName = type.includes('.') ? type.split('.')[1] : type;
253
+ if (typeName === 'Property' ||
254
+ typeName === 'DatatypeProperty' ||
255
+ typeName === 'ObjectProperty' ||
256
+ typeName === 'AnnotationProperty') {
257
+ isProperty = true;
258
+ propertyType = type; // Keep the full type (e.g., "rdf.Property" or "ObjectProperty")
259
+ break;
260
+ }
261
+ }
262
+ if (isProperty) {
263
+ const propInfo = { propertyType };
264
+ const rangeValue = resolution.entity['range'];
265
+ if (rangeValue) {
266
+ const range = rangeValue?.toString();
267
+ if (range && range.trim().length > 0) {
268
+ propInfo.range = range;
269
+ const rangeResolution = await resourceResolver.resolveEntityAsync(range, document);
270
+ if (rangeResolution) {
271
+ propInfo.rangeUri = rangeResolution.uri;
272
+ }
273
+ }
274
+ }
275
+ metadata.set(entityName, propInfo);
276
+ }
277
+ }
278
+ }
279
+ return metadata;
280
+ }
281
+ }
@@ -0,0 +1,14 @@
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 PropertyDomainRule implements IRepositoryValidationRule {
6
+ private readonly standardProperties;
7
+ get ruleName(): string;
8
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
9
+ private buildCompleteClassHierarchyAsync;
10
+ private validateEntityProperties;
11
+ private isTypeCompatibleWithDomain;
12
+ private getPropertyValue;
13
+ private extractFirstValue;
14
+ }
@@ -0,0 +1,221 @@
1
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
2
+ import { normalizeToStringList } from '../normalizeToStringList.js';
3
+ export class PropertyDomainRule {
4
+ standardProperties = new Set([
5
+ 'type',
6
+ 'label',
7
+ 'comment',
8
+ 'subClassOf',
9
+ 'domain',
10
+ 'range',
11
+ 'subPropertyOf',
12
+ 'inverseOf',
13
+ 'sameAs',
14
+ 'seeAlso',
15
+ 'isDefinedBy'
16
+ ]);
17
+ get ruleName() {
18
+ return 'PropertyDomain';
19
+ }
20
+ async validateAsync(document, repository) {
21
+ const errors = [];
22
+ const propertyDefinitions = new Map();
23
+ for (const [entityName, entityDef] of Object.entries(document.body)) {
24
+ if (typeof entityDef === 'object' && entityDef !== null && !Array.isArray(entityDef)) {
25
+ const entity = entityDef;
26
+ const type = this.getPropertyValue(entity, 'type');
27
+ if (type === 'DatatypeProperty' ||
28
+ type?.endsWith('.DatatypeProperty') ||
29
+ type === 'ObjectProperty' ||
30
+ type?.endsWith('.ObjectProperty') ||
31
+ type === 'AnnotationProperty' ||
32
+ type?.endsWith('.AnnotationProperty')) {
33
+ propertyDefinitions.set(entityName, entity);
34
+ }
35
+ }
36
+ }
37
+ const classHierarchy = await this.buildCompleteClassHierarchyAsync(document, repository);
38
+ for (const [entityName, entityDef] of Object.entries(document.body)) {
39
+ if (typeof entityDef === 'object' && entityDef !== null && !Array.isArray(entityDef)) {
40
+ const entity = entityDef;
41
+ const entityType = this.getPropertyValue(entity, 'type');
42
+ if (!entityType ||
43
+ entityType === 'Class' ||
44
+ entityType.endsWith('.Class') ||
45
+ entityType.endsWith('Property') ||
46
+ entityType === 'AnnotationProperty') {
47
+ continue;
48
+ }
49
+ this.validateEntityProperties(entity, entityName, entityType, propertyDefinitions, classHierarchy, errors);
50
+ }
51
+ }
52
+ return errors;
53
+ }
54
+ async buildCompleteClassHierarchyAsync(document, repository) {
55
+ const classHierarchy = new Map();
56
+ const visitedNamespaces = new Set();
57
+ const processDocumentAsync = async (doc, currentNamespace) => {
58
+ if (visitedNamespaces.has(currentNamespace)) {
59
+ return;
60
+ }
61
+ visitedNamespaces.add(currentNamespace);
62
+ for (const [entityName, entityDef] of Object.entries(doc.body)) {
63
+ if (typeof entityDef === 'object' && entityDef !== null && !Array.isArray(entityDef)) {
64
+ const entity = entityDef;
65
+ const type = this.getPropertyValue(entity, 'type');
66
+ if (type === 'Class' || type?.endsWith('.Class')) {
67
+ let subClassOfObj = entity['subClassOf'];
68
+ if (subClassOfObj === undefined) {
69
+ for (const key of Object.keys(entity)) {
70
+ if (key.endsWith('.subClassOf')) {
71
+ subClassOfObj = entity[key];
72
+ break;
73
+ }
74
+ }
75
+ }
76
+ const parentClasses = normalizeToStringList(subClassOfObj);
77
+ if (parentClasses.length > 0) {
78
+ classHierarchy.set(entityName, parentClasses);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ if (doc.metadata.imports) {
84
+ for (const [publisher, imports] of Object.entries(doc.metadata.imports)) {
85
+ for (const import_ of imports) {
86
+ try {
87
+ const importedDoc = await repository.getHighestCompatibleVersionAsync(publisher, import_);
88
+ if (importedDoc) {
89
+ const importedNamespace = `${publisher}/${import_.packageName}@${import_.version}`;
90
+ await processDocumentAsync(importedDoc, importedNamespace);
91
+ }
92
+ }
93
+ catch {
94
+ }
95
+ }
96
+ }
97
+ }
98
+ };
99
+ const currentNs = document.metadata.namespace_?.toString() ?? 'unknown';
100
+ await processDocumentAsync(document, currentNs);
101
+ return classHierarchy;
102
+ }
103
+ validateEntityProperties(entity, entityPath, entityType, propertyDefinitions, classHierarchy, errors) {
104
+ for (const [propName, propValue] of Object.entries(entity)) {
105
+ if (this.standardProperties.has(propName)) {
106
+ continue;
107
+ }
108
+ let lookupName = propName;
109
+ const dotIndex = propName.lastIndexOf('.');
110
+ if (dotIndex > 0 && dotIndex < propName.length - 1) {
111
+ lookupName = propName.substring(dotIndex + 1);
112
+ }
113
+ const propDef = propertyDefinitions.get(lookupName);
114
+ if (!propDef) {
115
+ continue;
116
+ }
117
+ const propertyDomain = this.getPropertyValue(propDef, 'domain');
118
+ if (!propertyDomain) {
119
+ continue;
120
+ }
121
+ if (!this.isTypeCompatibleWithDomain(entityType, propertyDomain, classHierarchy)) {
122
+ const rootEntity = entityPath.split('.')[0];
123
+ errors.push({
124
+ ruleType: this.ruleName,
125
+ severity: ValidationSeverity.Error,
126
+ entityName: rootEntity, // Root entity for lookup
127
+ propertyName: entityPath, // Full nested path for navigation
128
+ actualValue: propName, // Property to find at end of path
129
+ message: `Property '${propName}' has domain '${propertyDomain}' but is used on '${entityType}' at '${entityPath}'`,
130
+ suggestion: `Either:\n` +
131
+ ` 1. Use this property only on '${propertyDomain}' instances\n` +
132
+ ` 2. Change the property domain to include '${entityType}'\n` +
133
+ ` 3. Create a different property for '${entityType}'`,
134
+ expectedValue: `Property used on ${propertyDomain} or its subclasses`
135
+ });
136
+ }
137
+ if (typeof propValue === 'object' && propValue !== null && !Array.isArray(propValue)) {
138
+ const embeddedObj = propValue;
139
+ const embeddedType = this.getPropertyValue(embeddedObj, 'type');
140
+ if (embeddedType) {
141
+ const embeddedPath = `${entityPath}.${propName}`;
142
+ this.validateEntityProperties(embeddedObj, embeddedPath, embeddedType, propertyDefinitions, classHierarchy, errors);
143
+ }
144
+ else {
145
+ const propertyRange = this.getPropertyValue(propDef, 'range');
146
+ if (propertyRange) {
147
+ const embeddedPath = `${entityPath}.${propName}`;
148
+ this.validateEntityProperties(embeddedObj, embeddedPath, propertyRange, propertyDefinitions, classHierarchy, errors);
149
+ }
150
+ }
151
+ }
152
+ else if (Array.isArray(propValue)) {
153
+ for (let i = 0; i < propValue.length; i++) {
154
+ const item = propValue[i];
155
+ if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
156
+ const embeddedItem = item;
157
+ const embeddedType = this.getPropertyValue(embeddedItem, 'type');
158
+ if (embeddedType) {
159
+ const embeddedPath = `${entityPath}.${propName}[${i}]`;
160
+ this.validateEntityProperties(embeddedItem, embeddedPath, embeddedType, propertyDefinitions, classHierarchy, errors);
161
+ }
162
+ else {
163
+ const propertyRange = this.getPropertyValue(propDef, 'range');
164
+ if (propertyRange) {
165
+ const embeddedPath = `${entityPath}.${propName}[${i}]`;
166
+ this.validateEntityProperties(embeddedItem, embeddedPath, propertyRange, propertyDefinitions, classHierarchy, errors);
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ isTypeCompatibleWithDomain(type, domain, classHierarchy) {
175
+ if (type === domain) {
176
+ return true;
177
+ }
178
+ const visited = new Set();
179
+ const queue = [type];
180
+ while (queue.length > 0) {
181
+ const current = queue.shift();
182
+ if (visited.has(current)) {
183
+ continue;
184
+ }
185
+ visited.add(current);
186
+ const parentTypes = classHierarchy.get(current);
187
+ if (parentTypes) {
188
+ for (const parentType of parentTypes) {
189
+ if (parentType === domain) {
190
+ return true;
191
+ }
192
+ queue.push(parentType);
193
+ }
194
+ }
195
+ }
196
+ return false;
197
+ }
198
+ getPropertyValue(dict, propertyName) {
199
+ if (propertyName in dict) {
200
+ const value = dict[propertyName];
201
+ return this.extractFirstValue(value);
202
+ }
203
+ for (const key of Object.keys(dict)) {
204
+ if (key.endsWith(`.${propertyName}`)) {
205
+ const value = dict[key];
206
+ return this.extractFirstValue(value);
207
+ }
208
+ }
209
+ return undefined;
210
+ }
211
+ extractFirstValue(value) {
212
+ if (Array.isArray(value)) {
213
+ const items = [];
214
+ for (const item of value) {
215
+ items.push(item?.toString() ?? '');
216
+ }
217
+ return items.length > 0 ? items[0] : undefined;
218
+ }
219
+ return value?.toString();
220
+ }
221
+ }
@@ -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 PropertyHierarchyCycleRule implements IRepositoryValidationRule {
6
+ readonly ruleName = "PropertyHierarchyCycle";
7
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
8
+ private buildPropertyHierarchy;
9
+ private detectCycle;
10
+ private detectCycleRecursive;
11
+ }
@@ -0,0 +1,104 @@
1
+ import { OntologyValidationError } from '../../OntologyValidationError.js';
2
+ import { ValidationSeverity } from '../../ValidationSeverity.js';
3
+ import { normalizeToStringList } from '../normalizeToStringList.js';
4
+ export class PropertyHierarchyCycleRule {
5
+ ruleName = 'PropertyHierarchyCycle';
6
+ async validateAsync(document, repository) {
7
+ const errors = [];
8
+ const propertyHierarchy = new Map();
9
+ const visitedDocs = new Set();
10
+ await this.buildPropertyHierarchy(document, repository, propertyHierarchy, 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 subPropertyOfValue = entityDict['subPropertyOf'];
15
+ if (subPropertyOfValue) {
16
+ const propertyName = entityName;
17
+ const cycle = this.detectCycle(propertyName, propertyHierarchy);
18
+ if (cycle) {
19
+ const cycleChain = cycle.join(' → ');
20
+ const error = new OntologyValidationError();
21
+ error.ruleType = this.ruleName;
22
+ error.severity = ValidationSeverity.Error;
23
+ error.message = `Circular property hierarchy detected: ${cycleChain}`;
24
+ error.suggestion = 'Remove the circular dependency by breaking one of the subPropertyOf relationships';
25
+ error.entityName = propertyName;
26
+ error.propertyName = 'subPropertyOf';
27
+ error.actualValue = cycleChain;
28
+ errors.push(error);
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ }
34
+ return errors;
35
+ }
36
+ async buildPropertyHierarchy(document, repository, hierarchy, visitedDocs) {
37
+ const docId = document.metadata?.namespace_?.toString() ?? '';
38
+ if (docId && visitedDocs.has(docId)) {
39
+ return; // Already processed this document
40
+ }
41
+ if (docId) {
42
+ visitedDocs.add(docId);
43
+ }
44
+ for (const [entityName, entityValue] of Object.entries(document.body)) {
45
+ if (entityValue && typeof entityValue === 'object' && !Array.isArray(entityValue)) {
46
+ const entityDict = entityValue;
47
+ const typeValue = entityDict['type'];
48
+ if (typeValue) {
49
+ const typeStr = typeValue.toString();
50
+ if (typeStr === 'DatatypeProperty' ||
51
+ typeStr === 'ObjectProperty' ||
52
+ typeStr === 'AnnotationProperty') {
53
+ const subPropertyOfValue = entityDict['subPropertyOf'];
54
+ if (subPropertyOfValue) {
55
+ const parentProperties = normalizeToStringList(subPropertyOfValue);
56
+ if (parentProperties.length > 0) {
57
+ hierarchy.set(entityName, parentProperties);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ if (document.metadata?.imports) {
65
+ for (const [publisher, imports] of Object.entries(document.metadata.imports)) {
66
+ for (const import_ of imports) {
67
+ const importedDoc = await repository.getHighestCompatibleVersionAsync(publisher, import_);
68
+ if (importedDoc) {
69
+ await this.buildPropertyHierarchy(importedDoc, repository, hierarchy, visitedDocs);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ detectCycle(startProperty, hierarchy) {
76
+ const path = [];
77
+ const visited = new Set();
78
+ return this.detectCycleRecursive(startProperty, hierarchy, path, visited);
79
+ }
80
+ detectCycleRecursive(property, hierarchy, path, visited) {
81
+ if (path.includes(property)) {
82
+ const cycleStart = path.indexOf(property);
83
+ const cycle = path.slice(cycleStart);
84
+ cycle.push(property); // Complete the cycle
85
+ return cycle;
86
+ }
87
+ if (visited.has(property)) {
88
+ return null;
89
+ }
90
+ path.push(property);
91
+ visited.add(property);
92
+ const parentProperties = hierarchy.get(property);
93
+ if (parentProperties) {
94
+ for (const parentProperty of parentProperties) {
95
+ const cycle = this.detectCycleRecursive(parentProperty, hierarchy, path, visited);
96
+ if (cycle) {
97
+ return cycle;
98
+ }
99
+ }
100
+ }
101
+ path.pop();
102
+ return null;
103
+ }
104
+ }
@@ -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 PropertyRangeReferenceRule implements IRepositoryValidationRule {
6
+ private readonly builtInClasses;
7
+ private readonly primitiveTypes;
8
+ get ruleName(): string;
9
+ validateAsync(document: CanonDocument, repository: ICanonDocumentRepository): Promise<OntologyValidationError[]>;
10
+ private isTypeAvailableInImports;
11
+ private isTypeAvailableInImportsRecursive;
12
+ private isValidRangeTargetType;
13
+ }