@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,10 @@
1
+ import type { CtlDocument } from '@canon-protocol/types/ctl/models/types';
2
+ import type { CtlParser as ICtlParser } from '@canon-protocol/types/ctl/parsing/types';
3
+ export declare class CtlParser implements ICtlParser {
4
+ parse(content: string, filePath?: string | null): CtlDocument;
5
+ parseFileAsync(filePath: string): Promise<CtlDocument>;
6
+ private parseWithFrontmatter;
7
+ private parseInline;
8
+ private parseMarkdownBody;
9
+ private buildAliasMap;
10
+ }
@@ -0,0 +1,331 @@
1
+ import { VersionOperator } from '@canon-protocol/types/document/models/enums';
2
+ import { CtlCanonUri } from './CtlCanonUri.js';
3
+ import * as yaml from 'js-yaml';
4
+ const CANON_LINK_REGEX = /\[([^\]]+)\]\((canon:\/\/[^)]+)\)/gi;
5
+ const CAN_SHORT_LINK_REGEX = /\[([^\]]+)\]\(can:\/\/([^)]+)\)/gi;
6
+ const CODE_BLOCK_REGEX = /```[\s\S]*?```|`[^`]+`/g;
7
+ const FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---\s*\n/;
8
+ function createVersion(major, minor, patch) {
9
+ const versionStr = `${major}.${minor}.${patch}`;
10
+ return {
11
+ major,
12
+ minor,
13
+ patch,
14
+ toString: () => versionStr,
15
+ equals: (obj) => obj?.major === major && obj?.minor === minor && obj?.patch === patch,
16
+ getHashCode: () => (major << 20) | (minor << 10) | patch,
17
+ compareTo: (other) => {
18
+ if (major !== other.major)
19
+ return major - other.major;
20
+ if (minor !== other.minor)
21
+ return minor - other.minor;
22
+ return patch - other.patch;
23
+ }
24
+ };
25
+ }
26
+ function createImport(publisher, packageName, version, versionOperator, alias) {
27
+ const operatorSymbol = versionOperator === VersionOperator.Compatible ? '~' :
28
+ versionOperator === VersionOperator.Major ? '^' :
29
+ versionOperator === VersionOperator.Exact ? '=' : '*';
30
+ const package_ = `${packageName} ${operatorSymbol} ${version}`;
31
+ return {
32
+ package_,
33
+ publisher,
34
+ packageName,
35
+ versionOperator,
36
+ version,
37
+ alias,
38
+ get minVersion() { return version; },
39
+ get maxVersion() { return version; },
40
+ toEmbeddedObject() {
41
+ const obj = {
42
+ package: packageName,
43
+ version: version.toString()
44
+ };
45
+ if (alias)
46
+ obj.alias = alias;
47
+ if (versionOperator !== VersionOperator.Exact)
48
+ obj.match = operatorSymbol;
49
+ return obj;
50
+ },
51
+ toString() { return package_; }
52
+ };
53
+ }
54
+ export class CtlParser {
55
+ parse(content, filePath) {
56
+ if (content == null) {
57
+ throw new Error('Content cannot be null');
58
+ }
59
+ if (content.charCodeAt(0) === 0xFEFF) {
60
+ content = content.substring(1);
61
+ }
62
+ const frontmatterMatch = FRONTMATTER_REGEX.exec(content);
63
+ if (frontmatterMatch) {
64
+ return this.parseWithFrontmatter(content, frontmatterMatch, filePath ?? null);
65
+ }
66
+ else {
67
+ return this.parseInline(content, filePath ?? null);
68
+ }
69
+ }
70
+ async parseFileAsync(filePath) {
71
+ if (!filePath || filePath.trim().length === 0) {
72
+ throw new Error('File path cannot be null or empty');
73
+ }
74
+ const fs = await import('fs');
75
+ const content = fs.readFileSync(filePath, 'utf-8');
76
+ return this.parse(content, filePath);
77
+ }
78
+ parseWithFrontmatter(content, frontmatterMatch, filePath) {
79
+ const frontmatterYaml = frontmatterMatch[1];
80
+ const markdownBody = content.substring(frontmatterMatch[0].length);
81
+ let metadata = null;
82
+ let aliasMap = new Map();
83
+ try {
84
+ const yamlRoot = yaml.load(frontmatterYaml);
85
+ if (!yamlRoot) {
86
+ throw new Error('Frontmatter YAML is empty');
87
+ }
88
+ if (!yamlRoot.imports || !Array.isArray(yamlRoot.imports)) {
89
+ throw new Error("Frontmatter must contain an 'imports' sequence");
90
+ }
91
+ const importsByPublisher = {};
92
+ for (const entry of yamlRoot.imports) {
93
+ if (!entry || typeof entry !== 'object') {
94
+ throw new Error("Each import entry must be a mapping with 'publisher' and 'packages'");
95
+ }
96
+ const publisher = entry.publisher?.toString();
97
+ if (!publisher) {
98
+ throw new Error("Each import entry requires a 'publisher' property");
99
+ }
100
+ if (!entry.packages || !Array.isArray(entry.packages)) {
101
+ throw new Error(`Import entry for publisher '${publisher}' requires a 'packages' sequence`);
102
+ }
103
+ if (!importsByPublisher[publisher]) {
104
+ importsByPublisher[publisher] = [];
105
+ }
106
+ for (const pkgEntry of entry.packages) {
107
+ if (!pkgEntry || typeof pkgEntry !== 'object') {
108
+ throw new Error(`Each package in publisher '${publisher}' must be a mapping`);
109
+ }
110
+ const versionStr = pkgEntry.version?.toString() ?? '1.0.0';
111
+ const versionParts = versionStr.split('.').map(Number);
112
+ const version = createVersion(versionParts[0] || 0, versionParts[1] || 0, versionParts[2] || 0);
113
+ const matchStr = pkgEntry.match?.toString();
114
+ const versionOperator = matchStr === '^' ? VersionOperator.Major :
115
+ matchStr === '~' ? VersionOperator.Compatible :
116
+ matchStr === '*' ? VersionOperator.Any :
117
+ VersionOperator.Exact;
118
+ const import_ = createImport(publisher, pkgEntry.package?.toString() ?? '', version, versionOperator, pkgEntry.alias?.toString() ?? null);
119
+ importsByPublisher[publisher].push(import_);
120
+ }
121
+ }
122
+ const allImportsList = [];
123
+ for (const imports of Object.values(importsByPublisher)) {
124
+ allImportsList.push(...imports);
125
+ }
126
+ metadata = {
127
+ namespace_: undefined,
128
+ imports: importsByPublisher,
129
+ get allImports() { return allImportsList; }
130
+ };
131
+ aliasMap = this.buildAliasMap(importsByPublisher);
132
+ }
133
+ catch (ex) {
134
+ const errorRef = {
135
+ displayText: '(frontmatter)',
136
+ rawUri: '',
137
+ startOffset: 0,
138
+ endOffset: frontmatterMatch[0].length,
139
+ line: 1,
140
+ column: 1,
141
+ isResolved: false,
142
+ error: `Failed to parse frontmatter: ${ex.message}`,
143
+ get fullMarkdownLink() { return `[${this.displayText}](${this.rawUri})`; }
144
+ };
145
+ return createCtlDocument({
146
+ metadata: null,
147
+ rawMarkdown: markdownBody,
148
+ frontmatterLength: frontmatterMatch[0].length,
149
+ filePath,
150
+ references: [errorRef]
151
+ });
152
+ }
153
+ const bodyReferences = this.parseMarkdownBody(markdownBody, aliasMap, frontmatterMatch[0].length);
154
+ return createCtlDocument({
155
+ metadata,
156
+ rawMarkdown: markdownBody,
157
+ frontmatterLength: frontmatterMatch[0].length,
158
+ filePath,
159
+ references: bodyReferences
160
+ });
161
+ }
162
+ parseInline(content, filePath) {
163
+ const references = this.parseMarkdownBody(content, new Map(), 0);
164
+ return createCtlDocument({
165
+ metadata: null,
166
+ rawMarkdown: content,
167
+ frontmatterLength: 0,
168
+ filePath,
169
+ references
170
+ });
171
+ }
172
+ parseMarkdownBody(markdown, aliasMap, offsetAdjustment) {
173
+ const references = [];
174
+ const codeBlockRanges = findCodeBlockRanges(markdown);
175
+ const canonRegex = new RegExp(CANON_LINK_REGEX.source, 'gi');
176
+ let match;
177
+ while ((match = canonRegex.exec(markdown)) !== null) {
178
+ if (isInsideCodeBlock(match.index, codeBlockRanges))
179
+ continue;
180
+ const displayText = match[1];
181
+ const rawUri = match[2];
182
+ const { line, column } = getLineAndColumn(markdown, match.index);
183
+ const reference = {
184
+ displayText,
185
+ rawUri,
186
+ startOffset: match.index + offsetAdjustment,
187
+ endOffset: match.index + match[0].length + offsetAdjustment,
188
+ line,
189
+ column,
190
+ isResolved: false,
191
+ get fullMarkdownLink() { return `[${this.displayText}](${this.rawUri})`; }
192
+ };
193
+ try {
194
+ reference.parsedUri = CtlCanonUri.parse(rawUri);
195
+ }
196
+ catch (ex) {
197
+ reference.error = ex.message;
198
+ }
199
+ references.push(reference);
200
+ }
201
+ const canShortRegex = new RegExp(CAN_SHORT_LINK_REGEX.source, 'gi');
202
+ while ((match = canShortRegex.exec(markdown)) !== null) {
203
+ if (isInsideCodeBlock(match.index, codeBlockRanges))
204
+ continue;
205
+ const displayText = match[1];
206
+ const linkPath = match[2];
207
+ const { line, column } = getLineAndColumn(markdown, match.index);
208
+ let resolvedUri = null;
209
+ let error = null;
210
+ try {
211
+ resolvedUri = resolveCanLink(linkPath, aliasMap);
212
+ }
213
+ catch (ex) {
214
+ error = ex.message;
215
+ }
216
+ const reference = {
217
+ displayText,
218
+ rawUri: resolvedUri ?? `can://${linkPath}`,
219
+ startOffset: match.index + offsetAdjustment,
220
+ endOffset: match.index + match[0].length + offsetAdjustment,
221
+ line,
222
+ column,
223
+ isResolved: false,
224
+ error,
225
+ get fullMarkdownLink() { return `[${this.displayText}](${this.rawUri})`; }
226
+ };
227
+ if (resolvedUri != null && error == null) {
228
+ try {
229
+ reference.parsedUri = CtlCanonUri.parse(resolvedUri);
230
+ }
231
+ catch (ex) {
232
+ reference.error = ex.message;
233
+ }
234
+ }
235
+ references.push(reference);
236
+ }
237
+ references.sort((a, b) => a.startOffset - b.startOffset);
238
+ return references;
239
+ }
240
+ buildAliasMap(imports) {
241
+ const aliasMap = new Map();
242
+ for (const [publisher, importList] of Object.entries(imports)) {
243
+ for (const import_ of importList) {
244
+ const alias = import_.alias ?? import_.packageName;
245
+ aliasMap.set(alias.toLowerCase(), {
246
+ publisher,
247
+ package_: import_.packageName,
248
+ version: import_.version
249
+ });
250
+ }
251
+ }
252
+ return aliasMap;
253
+ }
254
+ }
255
+ function createCtlDocument(init) {
256
+ return {
257
+ metadata: init.metadata,
258
+ get hasFrontmatter() { return init.metadata != null; },
259
+ rawMarkdown: init.rawMarkdown,
260
+ frontmatterLength: init.frontmatterLength,
261
+ filePath: init.filePath,
262
+ references: init.references,
263
+ get referenceCount() { return init.references.length; },
264
+ get allReferencesResolved() { return init.references.every((r) => r.isResolved); },
265
+ get unresolvedReferences() { return init.references.filter((r) => !r.isResolved); },
266
+ get errorReferences() { return init.references.filter((r) => r.error != null); }
267
+ };
268
+ }
269
+ function findCodeBlockRanges(markdown) {
270
+ const ranges = [];
271
+ const regex = new RegExp(CODE_BLOCK_REGEX.source, 'g');
272
+ let match;
273
+ while ((match = regex.exec(markdown)) !== null) {
274
+ ranges.push([match.index, match.index + match[0].length]);
275
+ }
276
+ return ranges;
277
+ }
278
+ function isInsideCodeBlock(offset, codeBlockRanges) {
279
+ for (const [start, end] of codeBlockRanges) {
280
+ if (offset >= start && offset < end)
281
+ return true;
282
+ }
283
+ return false;
284
+ }
285
+ function getLineAndColumn(text, offset) {
286
+ let line = 1;
287
+ let column = 1;
288
+ for (let i = 0; i < offset && i < text.length; i++) {
289
+ if (text[i] === '\n') {
290
+ line++;
291
+ column = 1;
292
+ }
293
+ else {
294
+ column++;
295
+ }
296
+ }
297
+ return { line, column };
298
+ }
299
+ function resolveCanLink(linkPath, aliasMap) {
300
+ if (!linkPath || linkPath.trim().length === 0) {
301
+ throw new Error('can:// link cannot be empty');
302
+ }
303
+ if (aliasMap.size === 0) {
304
+ throw new Error('can:// links require frontmatter with imports');
305
+ }
306
+ let alias = null;
307
+ let entityName = linkPath;
308
+ const dotIndex = linkPath.indexOf('.');
309
+ if (dotIndex > 0) {
310
+ alias = linkPath.substring(0, dotIndex);
311
+ entityName = linkPath.substring(dotIndex + 1);
312
+ if (!entityName || entityName.trim().length === 0) {
313
+ throw new Error(`Resource name required after alias '${alias}.'`);
314
+ }
315
+ }
316
+ if (alias != null) {
317
+ const import_ = aliasMap.get(alias.toLowerCase());
318
+ if (!import_) {
319
+ const availableAliases = Array.from(aliasMap.keys()).join(', ');
320
+ throw new Error(`Unknown alias: '${alias}'. Available: ${availableAliases}`);
321
+ }
322
+ return `canon://${import_.publisher}/${import_.package_}@${import_.version}/${entityName}`;
323
+ }
324
+ else {
325
+ const firstImport = aliasMap.values().next().value;
326
+ if (!firstImport) {
327
+ throw new Error(`Cannot resolve '${entityName}': no imports defined`);
328
+ }
329
+ return `canon://${firstImport.publisher}/${firstImport.package_}@${firstImport.version}/${entityName}`;
330
+ }
331
+ }
@@ -0,0 +1,12 @@
1
+ import type { CtlDocument, CtlValidationResult } from '@canon-protocol/types/ctl/models/types';
2
+ import type { CtlValidator as ICtlValidator } from '@canon-protocol/types/ctl/validation/types';
3
+ import type { ICanonDocumentRepository } from '@canon-protocol/types/document/models';
4
+ export declare class CtlValidator implements ICtlValidator {
5
+ private readonly repository;
6
+ constructor(repository: ICanonDocumentRepository);
7
+ validateAsync(document: CtlDocument): Promise<CtlValidationResult>;
8
+ private resolveReference;
9
+ private resolveResource;
10
+ private resolveDeepLink;
11
+ private createError;
12
+ }
@@ -0,0 +1,207 @@
1
+ import { CtlValidationErrorType, CtlValidationSeverity } from '@canon-protocol/types/ctl/models/enums';
2
+ import { CtlCanonUriType, PathSegmentType } from '@canon-protocol/types/ctl/parsing/enums';
3
+ export class CtlValidator {
4
+ repository;
5
+ constructor(repository) {
6
+ if (!repository) {
7
+ throw new Error('Repository is required for CTL validation');
8
+ }
9
+ this.repository = repository;
10
+ }
11
+ async validateAsync(document) {
12
+ if (!document) {
13
+ throw new Error('Document cannot be null');
14
+ }
15
+ const errors = [];
16
+ const warnings = [];
17
+ for (const reference of document.references) {
18
+ if (!reference.parsedUri) {
19
+ errors.push({
20
+ errorType: CtlValidationErrorType.InvalidUri,
21
+ severity: CtlValidationSeverity.Error,
22
+ message: reference.error ?? 'Failed to parse Canon URI',
23
+ reference,
24
+ line: reference.line,
25
+ column: reference.column,
26
+ toString() {
27
+ const location = this.line != null ? `(${this.line}:${this.column})` : '';
28
+ return `${CtlValidationSeverity[this.severity]}: ${this.message} ${location}`.trim();
29
+ }
30
+ });
31
+ continue;
32
+ }
33
+ try {
34
+ await this.resolveReference(reference, errors, warnings);
35
+ }
36
+ catch (ex) {
37
+ errors.push({
38
+ errorType: CtlValidationErrorType.ParseError,
39
+ severity: CtlValidationSeverity.Error,
40
+ message: `Error resolving reference: ${ex.message}`,
41
+ reference,
42
+ line: reference.line,
43
+ column: reference.column,
44
+ toString() {
45
+ const location = this.line != null ? `(${this.line}:${this.column})` : '';
46
+ return `${CtlValidationSeverity[this.severity]}: ${this.message} ${location}`.trim();
47
+ }
48
+ });
49
+ }
50
+ }
51
+ const result = {
52
+ isValid: errors.length === 0,
53
+ document,
54
+ errors,
55
+ warnings,
56
+ get totalReferences() { return document.referenceCount; },
57
+ get resolvedReferences() { return document.references.filter((r) => r.isResolved).length; },
58
+ get summary() {
59
+ if (errors.length === 0) {
60
+ return `All ${document.referenceCount} references validated`;
61
+ }
62
+ const resolvedCount = document.references.filter((r) => r.isResolved).length;
63
+ return `${errors.length} error(s), ${resolvedCount}/${document.referenceCount} references resolved`;
64
+ }
65
+ };
66
+ return result;
67
+ }
68
+ async resolveReference(reference, errors, _warnings) {
69
+ const uri = reference.parsedUri;
70
+ switch (uri.uriType) {
71
+ case CtlCanonUriType.Resource:
72
+ await this.resolveResource(reference, errors);
73
+ break;
74
+ case CtlCanonUriType.DeepLink:
75
+ await this.resolveDeepLink(reference, errors);
76
+ break;
77
+ default:
78
+ errors.push({
79
+ errorType: CtlValidationErrorType.InvalidUri,
80
+ severity: CtlValidationSeverity.Error,
81
+ message: `Unsupported URI type: ${uri.uriType}. CTL only supports resource references.`,
82
+ reference,
83
+ line: reference.line,
84
+ column: reference.column,
85
+ suggestion: 'Use resource URIs: canon://publisher/package@version/resource',
86
+ toString() {
87
+ const location = this.line != null ? `(${this.line}:${this.column})` : '';
88
+ return `${CtlValidationSeverity[this.severity]}: ${this.message} ${location}`.trim();
89
+ }
90
+ });
91
+ break;
92
+ }
93
+ }
94
+ async resolveResource(reference, errors) {
95
+ const uri = reference.parsedUri;
96
+ const namespaceStr = uri.namespace_;
97
+ const doc = await this.repository.getDocumentAsync(namespaceStr);
98
+ if (!doc) {
99
+ errors.push(this.createError(CtlValidationErrorType.PackageNotFound, `Package not found: ${namespaceStr}`, reference));
100
+ return;
101
+ }
102
+ if (doc.body && doc.body[uri.resourceName]) {
103
+ reference.isResolved = true;
104
+ }
105
+ else {
106
+ const availableEntities = doc.body
107
+ ? Object.keys(doc.body).slice(0, 5)
108
+ : [];
109
+ errors.push(this.createError(CtlValidationErrorType.EntityNotFound, `Resource not found: ${uri.resourceName} in ${namespaceStr}`, reference, availableEntities.length > 0
110
+ ? `Available resources: ${availableEntities.join(', ')}`
111
+ : undefined));
112
+ }
113
+ }
114
+ async resolveDeepLink(reference, errors) {
115
+ const uri = reference.parsedUri;
116
+ const namespaceStr = uri.namespace_;
117
+ const doc = await this.repository.getDocumentAsync(namespaceStr);
118
+ if (!doc) {
119
+ errors.push(this.createError(CtlValidationErrorType.PackageNotFound, `Package not found: ${namespaceStr}`, reference));
120
+ return;
121
+ }
122
+ if (!doc.body || !doc.body[uri.resourceName]) {
123
+ errors.push(this.createError(CtlValidationErrorType.EntityNotFound, `Resource not found: ${uri.resourceName} in ${namespaceStr}`, reference));
124
+ return;
125
+ }
126
+ let current = doc.body[uri.resourceName];
127
+ let pathSoFar = uri.resourceName;
128
+ for (const segment of uri.path) {
129
+ if (current == null) {
130
+ errors.push(this.createError(CtlValidationErrorType.PathNotFound, `Path not found: ${pathSoFar}`, reference));
131
+ return;
132
+ }
133
+ if (segment.type_ === PathSegmentType.Property) {
134
+ const propertyName = segment.name;
135
+ pathSoFar += `.${propertyName}`;
136
+ const nextValue = navigateToProperty(current, propertyName);
137
+ if (nextValue === undefined) {
138
+ const suggestion = getAvailablePropertiesSuggestion(current);
139
+ errors.push(this.createError(CtlValidationErrorType.PathNotFound, `Path not found: ${pathSoFar}`, reference, suggestion));
140
+ return;
141
+ }
142
+ current = nextValue;
143
+ }
144
+ else if (segment.type_ === PathSegmentType.Index) {
145
+ const index = segment.index;
146
+ pathSoFar += `[${index}]`;
147
+ const nextValue = navigateToIndex(current, index);
148
+ if (nextValue === undefined) {
149
+ const suggestion = getCollectionSizeSuggestion(current);
150
+ errors.push(this.createError(CtlValidationErrorType.IndexOutOfBounds, `Index out of bounds: ${pathSoFar}`, reference, suggestion));
151
+ return;
152
+ }
153
+ current = nextValue;
154
+ }
155
+ }
156
+ reference.isResolved = true;
157
+ reference.resolvedValue = current;
158
+ }
159
+ createError(errorType, message, reference, suggestion) {
160
+ return {
161
+ errorType,
162
+ severity: CtlValidationSeverity.Error,
163
+ message,
164
+ reference,
165
+ line: reference.line,
166
+ column: reference.column,
167
+ suggestion,
168
+ toString() {
169
+ const location = this.line != null ? `(${this.line}:${this.column})` : '';
170
+ return `${CtlValidationSeverity[this.severity]}: ${this.message} ${location}`.trim();
171
+ }
172
+ };
173
+ }
174
+ }
175
+ function navigateToProperty(current, propertyName) {
176
+ if (current && typeof current === 'object' && !Array.isArray(current)) {
177
+ if (propertyName in current)
178
+ return current[propertyName];
179
+ const key = Object.keys(current).find((k) => k.toLowerCase() === propertyName.toLowerCase());
180
+ if (key)
181
+ return current[key];
182
+ }
183
+ return undefined;
184
+ }
185
+ function navigateToIndex(current, index) {
186
+ if (Array.isArray(current)) {
187
+ if (index >= 0 && index < current.length)
188
+ return current[index];
189
+ }
190
+ return undefined;
191
+ }
192
+ function getAvailablePropertiesSuggestion(current) {
193
+ if (current && typeof current === 'object' && !Array.isArray(current)) {
194
+ const keys = Object.keys(current).slice(0, 5);
195
+ if (keys.length > 0)
196
+ return `Available properties: ${keys.join(', ')}`;
197
+ }
198
+ return undefined;
199
+ }
200
+ function getCollectionSizeSuggestion(current) {
201
+ if (Array.isArray(current)) {
202
+ return current.length > 0
203
+ ? `Collection has ${current.length} items (valid indices: 0-${current.length - 1})`
204
+ : 'Collection is empty';
205
+ }
206
+ return undefined;
207
+ }
@@ -0,0 +1,21 @@
1
+ import type { SubjectCanon } from '@canon-protocol/types/object/canons/types';
2
+ export declare class ResourceTypeClassifier {
3
+ static getTypeUri(resource: SubjectCanon): {
4
+ publisher: string;
5
+ package_: string;
6
+ name: string;
7
+ } | null;
8
+ static isCoreOntologyType(uri: {
9
+ publisher: string;
10
+ package_: string;
11
+ }): boolean;
12
+ static isClassType(resource: SubjectCanon): boolean;
13
+ static isDatatypeType(resource: SubjectCanon): boolean;
14
+ static isDatatypePropertyType(resource: SubjectCanon): boolean;
15
+ static isObjectPropertyType(resource: SubjectCanon): boolean;
16
+ static isAnnotationPropertyType(resource: SubjectCanon): boolean;
17
+ static isGenericPropertyType(resource: SubjectCanon): boolean;
18
+ static isAnyPropertyType(resource: SubjectCanon): boolean;
19
+ static isSchemaDefinitionType(resource: SubjectCanon): boolean;
20
+ static isInstanceOfKnownClass(resource: SubjectCanon, knownClassNames: Set<string>): boolean;
21
+ }