@rejot-dev/thalo 0.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 (237) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/dist/ast/ast-types.d.ts +469 -0
  4. package/dist/ast/ast-types.d.ts.map +1 -0
  5. package/dist/ast/ast-types.js +11 -0
  6. package/dist/ast/ast-types.js.map +1 -0
  7. package/dist/ast/builder.js +158 -0
  8. package/dist/ast/builder.js.map +1 -0
  9. package/dist/ast/extract.js +748 -0
  10. package/dist/ast/extract.js.map +1 -0
  11. package/dist/ast/node-at-position.d.ts +147 -0
  12. package/dist/ast/node-at-position.d.ts.map +1 -0
  13. package/dist/ast/node-at-position.js +382 -0
  14. package/dist/ast/node-at-position.js.map +1 -0
  15. package/dist/ast/visitor.js +232 -0
  16. package/dist/ast/visitor.js.map +1 -0
  17. package/dist/checker/check.d.ts +53 -0
  18. package/dist/checker/check.d.ts.map +1 -0
  19. package/dist/checker/check.js +105 -0
  20. package/dist/checker/check.js.map +1 -0
  21. package/dist/checker/rules/actualize-missing-updated.js +34 -0
  22. package/dist/checker/rules/actualize-missing-updated.js.map +1 -0
  23. package/dist/checker/rules/actualize-unresolved-target.js +42 -0
  24. package/dist/checker/rules/actualize-unresolved-target.js.map +1 -0
  25. package/dist/checker/rules/alter-before-define.js +53 -0
  26. package/dist/checker/rules/alter-before-define.js.map +1 -0
  27. package/dist/checker/rules/alter-undefined-entity.js +32 -0
  28. package/dist/checker/rules/alter-undefined-entity.js.map +1 -0
  29. package/dist/checker/rules/create-requires-section.js +34 -0
  30. package/dist/checker/rules/create-requires-section.js.map +1 -0
  31. package/dist/checker/rules/define-entity-requires-section.js +31 -0
  32. package/dist/checker/rules/define-entity-requires-section.js.map +1 -0
  33. package/dist/checker/rules/duplicate-entity-definition.js +37 -0
  34. package/dist/checker/rules/duplicate-entity-definition.js.map +1 -0
  35. package/dist/checker/rules/duplicate-field-in-schema.js +38 -0
  36. package/dist/checker/rules/duplicate-field-in-schema.js.map +1 -0
  37. package/dist/checker/rules/duplicate-link-id.js +52 -0
  38. package/dist/checker/rules/duplicate-link-id.js.map +1 -0
  39. package/dist/checker/rules/duplicate-metadata-key.js +21 -0
  40. package/dist/checker/rules/duplicate-metadata-key.js.map +1 -0
  41. package/dist/checker/rules/duplicate-section-heading.js +41 -0
  42. package/dist/checker/rules/duplicate-section-heading.js.map +1 -0
  43. package/dist/checker/rules/duplicate-section-in-schema.js +38 -0
  44. package/dist/checker/rules/duplicate-section-in-schema.js.map +1 -0
  45. package/dist/checker/rules/duplicate-timestamp.js +104 -0
  46. package/dist/checker/rules/duplicate-timestamp.js.map +1 -0
  47. package/dist/checker/rules/empty-required-value.js +45 -0
  48. package/dist/checker/rules/empty-required-value.js.map +1 -0
  49. package/dist/checker/rules/empty-section.js +21 -0
  50. package/dist/checker/rules/empty-section.js.map +1 -0
  51. package/dist/checker/rules/invalid-date-range-value.js +56 -0
  52. package/dist/checker/rules/invalid-date-range-value.js.map +1 -0
  53. package/dist/checker/rules/invalid-default-value.js +86 -0
  54. package/dist/checker/rules/invalid-default-value.js.map +1 -0
  55. package/dist/checker/rules/invalid-field-type.js +45 -0
  56. package/dist/checker/rules/invalid-field-type.js.map +1 -0
  57. package/dist/checker/rules/missing-required-field.js +48 -0
  58. package/dist/checker/rules/missing-required-field.js.map +1 -0
  59. package/dist/checker/rules/missing-required-section.js +51 -0
  60. package/dist/checker/rules/missing-required-section.js.map +1 -0
  61. package/dist/checker/rules/missing-title.js +56 -0
  62. package/dist/checker/rules/missing-title.js.map +1 -0
  63. package/dist/checker/rules/remove-undefined-field.js +42 -0
  64. package/dist/checker/rules/remove-undefined-field.js.map +1 -0
  65. package/dist/checker/rules/remove-undefined-section.js +42 -0
  66. package/dist/checker/rules/remove-undefined-section.js.map +1 -0
  67. package/dist/checker/rules/rules.d.ts +71 -0
  68. package/dist/checker/rules/rules.d.ts.map +1 -0
  69. package/dist/checker/rules/rules.js +102 -0
  70. package/dist/checker/rules/rules.js.map +1 -0
  71. package/dist/checker/rules/synthesis-empty-query.js +35 -0
  72. package/dist/checker/rules/synthesis-empty-query.js.map +1 -0
  73. package/dist/checker/rules/synthesis-missing-prompt.js +42 -0
  74. package/dist/checker/rules/synthesis-missing-prompt.js.map +1 -0
  75. package/dist/checker/rules/synthesis-missing-sources.js +32 -0
  76. package/dist/checker/rules/synthesis-missing-sources.js.map +1 -0
  77. package/dist/checker/rules/synthesis-unknown-query-entity.js +39 -0
  78. package/dist/checker/rules/synthesis-unknown-query-entity.js.map +1 -0
  79. package/dist/checker/rules/timestamp-out-of-order.js +55 -0
  80. package/dist/checker/rules/timestamp-out-of-order.js.map +1 -0
  81. package/dist/checker/rules/unknown-entity.js +32 -0
  82. package/dist/checker/rules/unknown-entity.js.map +1 -0
  83. package/dist/checker/rules/unknown-field.js +40 -0
  84. package/dist/checker/rules/unknown-field.js.map +1 -0
  85. package/dist/checker/rules/unknown-section.js +47 -0
  86. package/dist/checker/rules/unknown-section.js.map +1 -0
  87. package/dist/checker/rules/unresolved-link.js +34 -0
  88. package/dist/checker/rules/unresolved-link.js.map +1 -0
  89. package/dist/checker/rules/update-without-create.js +65 -0
  90. package/dist/checker/rules/update-without-create.js.map +1 -0
  91. package/dist/checker/visitor.d.ts +69 -0
  92. package/dist/checker/visitor.d.ts.map +1 -0
  93. package/dist/checker/visitor.js +67 -0
  94. package/dist/checker/visitor.js.map +1 -0
  95. package/dist/checker/workspace-index.d.ts +50 -0
  96. package/dist/checker/workspace-index.d.ts.map +1 -0
  97. package/dist/checker/workspace-index.js +108 -0
  98. package/dist/checker/workspace-index.js.map +1 -0
  99. package/dist/commands/actualize.d.ts +113 -0
  100. package/dist/commands/actualize.d.ts.map +1 -0
  101. package/dist/commands/actualize.js +111 -0
  102. package/dist/commands/actualize.js.map +1 -0
  103. package/dist/commands/check.d.ts +65 -0
  104. package/dist/commands/check.d.ts.map +1 -0
  105. package/dist/commands/check.js +61 -0
  106. package/dist/commands/check.js.map +1 -0
  107. package/dist/commands/format.d.ts +90 -0
  108. package/dist/commands/format.d.ts.map +1 -0
  109. package/dist/commands/format.js +80 -0
  110. package/dist/commands/format.js.map +1 -0
  111. package/dist/commands/query.d.ts +152 -0
  112. package/dist/commands/query.d.ts.map +1 -0
  113. package/dist/commands/query.js +151 -0
  114. package/dist/commands/query.js.map +1 -0
  115. package/dist/constants.d.ts +31 -0
  116. package/dist/constants.d.ts.map +1 -0
  117. package/dist/constants.js +51 -0
  118. package/dist/constants.js.map +1 -0
  119. package/dist/files.d.ts +58 -0
  120. package/dist/files.d.ts.map +1 -0
  121. package/dist/files.js +103 -0
  122. package/dist/files.js.map +1 -0
  123. package/dist/formatters.d.ts +39 -0
  124. package/dist/formatters.d.ts.map +1 -0
  125. package/dist/formatters.js +200 -0
  126. package/dist/formatters.js.map +1 -0
  127. package/dist/fragment.d.ts +22 -0
  128. package/dist/fragment.d.ts.map +1 -0
  129. package/dist/git/git.js +240 -0
  130. package/dist/git/git.js.map +1 -0
  131. package/dist/merge/conflict-detector.d.ts +89 -0
  132. package/dist/merge/conflict-detector.d.ts.map +1 -0
  133. package/dist/merge/conflict-detector.js +352 -0
  134. package/dist/merge/conflict-detector.js.map +1 -0
  135. package/dist/merge/conflict-formatter.js +143 -0
  136. package/dist/merge/conflict-formatter.js.map +1 -0
  137. package/dist/merge/driver.d.ts +54 -0
  138. package/dist/merge/driver.d.ts.map +1 -0
  139. package/dist/merge/driver.js +112 -0
  140. package/dist/merge/driver.js.map +1 -0
  141. package/dist/merge/entry-matcher.d.ts +50 -0
  142. package/dist/merge/entry-matcher.d.ts.map +1 -0
  143. package/dist/merge/entry-matcher.js +141 -0
  144. package/dist/merge/entry-matcher.js.map +1 -0
  145. package/dist/merge/entry-merger.js +194 -0
  146. package/dist/merge/entry-merger.js.map +1 -0
  147. package/dist/merge/merge-result-builder.d.ts +62 -0
  148. package/dist/merge/merge-result-builder.d.ts.map +1 -0
  149. package/dist/merge/merge-result-builder.js +89 -0
  150. package/dist/merge/merge-result-builder.js.map +1 -0
  151. package/dist/mod.d.ts +31 -0
  152. package/dist/mod.js +23 -0
  153. package/dist/model/document.d.ts +134 -0
  154. package/dist/model/document.d.ts.map +1 -0
  155. package/dist/model/document.js +275 -0
  156. package/dist/model/document.js.map +1 -0
  157. package/dist/model/line-index.d.ts +85 -0
  158. package/dist/model/line-index.d.ts.map +1 -0
  159. package/dist/model/line-index.js +159 -0
  160. package/dist/model/line-index.js.map +1 -0
  161. package/dist/model/workspace.d.ts +296 -0
  162. package/dist/model/workspace.d.ts.map +1 -0
  163. package/dist/model/workspace.js +562 -0
  164. package/dist/model/workspace.js.map +1 -0
  165. package/dist/parser.js +27 -0
  166. package/dist/parser.js.map +1 -0
  167. package/dist/parser.native.d.ts +51 -0
  168. package/dist/parser.native.d.ts.map +1 -0
  169. package/dist/parser.native.js +62 -0
  170. package/dist/parser.native.js.map +1 -0
  171. package/dist/parser.shared.d.ts +99 -0
  172. package/dist/parser.shared.d.ts.map +1 -0
  173. package/dist/parser.shared.js +124 -0
  174. package/dist/parser.shared.js.map +1 -0
  175. package/dist/parser.web.d.ts +67 -0
  176. package/dist/parser.web.d.ts.map +1 -0
  177. package/dist/parser.web.js +49 -0
  178. package/dist/parser.web.js.map +1 -0
  179. package/dist/schema/registry.d.ts +108 -0
  180. package/dist/schema/registry.d.ts.map +1 -0
  181. package/dist/schema/registry.js +281 -0
  182. package/dist/schema/registry.js.map +1 -0
  183. package/dist/semantic/analyzer.d.ts +107 -0
  184. package/dist/semantic/analyzer.d.ts.map +1 -0
  185. package/dist/semantic/analyzer.js +261 -0
  186. package/dist/semantic/analyzer.js.map +1 -0
  187. package/dist/services/change-tracker/change-tracker.d.ts +111 -0
  188. package/dist/services/change-tracker/change-tracker.d.ts.map +1 -0
  189. package/dist/services/change-tracker/change-tracker.js +62 -0
  190. package/dist/services/change-tracker/change-tracker.js.map +1 -0
  191. package/dist/services/change-tracker/create-tracker.d.ts +42 -0
  192. package/dist/services/change-tracker/create-tracker.d.ts.map +1 -0
  193. package/dist/services/change-tracker/create-tracker.js +53 -0
  194. package/dist/services/change-tracker/create-tracker.js.map +1 -0
  195. package/dist/services/change-tracker/git-tracker.d.ts +59 -0
  196. package/dist/services/change-tracker/git-tracker.d.ts.map +1 -0
  197. package/dist/services/change-tracker/git-tracker.js +218 -0
  198. package/dist/services/change-tracker/git-tracker.js.map +1 -0
  199. package/dist/services/change-tracker/timestamp-tracker.d.ts +22 -0
  200. package/dist/services/change-tracker/timestamp-tracker.d.ts.map +1 -0
  201. package/dist/services/change-tracker/timestamp-tracker.js +74 -0
  202. package/dist/services/change-tracker/timestamp-tracker.js.map +1 -0
  203. package/dist/services/definition.d.ts +37 -0
  204. package/dist/services/definition.d.ts.map +1 -0
  205. package/dist/services/definition.js +43 -0
  206. package/dist/services/definition.js.map +1 -0
  207. package/dist/services/entity-navigation.d.ts +200 -0
  208. package/dist/services/entity-navigation.d.ts.map +1 -0
  209. package/dist/services/entity-navigation.js +211 -0
  210. package/dist/services/entity-navigation.js.map +1 -0
  211. package/dist/services/hover.d.ts +81 -0
  212. package/dist/services/hover.d.ts.map +1 -0
  213. package/dist/services/hover.js +669 -0
  214. package/dist/services/hover.js.map +1 -0
  215. package/dist/services/query.d.ts +116 -0
  216. package/dist/services/query.d.ts.map +1 -0
  217. package/dist/services/query.js +225 -0
  218. package/dist/services/query.js.map +1 -0
  219. package/dist/services/references.d.ts +52 -0
  220. package/dist/services/references.d.ts.map +1 -0
  221. package/dist/services/references.js +66 -0
  222. package/dist/services/references.js.map +1 -0
  223. package/dist/services/semantic-tokens.d.ts +54 -0
  224. package/dist/services/semantic-tokens.d.ts.map +1 -0
  225. package/dist/services/semantic-tokens.js +213 -0
  226. package/dist/services/semantic-tokens.js.map +1 -0
  227. package/dist/services/synthesis.d.ts +90 -0
  228. package/dist/services/synthesis.d.ts.map +1 -0
  229. package/dist/services/synthesis.js +113 -0
  230. package/dist/services/synthesis.js.map +1 -0
  231. package/dist/source-map.d.ts +42 -0
  232. package/dist/source-map.d.ts.map +1 -0
  233. package/dist/source-map.js +170 -0
  234. package/dist/source-map.js.map +1 -0
  235. package/package.json +128 -0
  236. package/tree-sitter-thalo.wasm +0 -0
  237. package/web-tree-sitter.wasm +0 -0
@@ -0,0 +1,21 @@
1
+ //#region src/checker/rules/empty-section.ts
2
+ const category = "content";
3
+ /**
4
+ * Check for section headings that have no content
5
+ *
6
+ * Note: This rule is a placeholder. Detecting empty sections requires access to
7
+ * section boundaries in the AST, which isn't currently available.
8
+ */
9
+ const emptySectionRule = {
10
+ code: "empty-section",
11
+ name: "Empty Section",
12
+ description: "Section heading exists but has no content",
13
+ category,
14
+ defaultSeverity: "warning",
15
+ dependencies: { scope: "entry" },
16
+ visitor: {}
17
+ };
18
+
19
+ //#endregion
20
+ export { emptySectionRule };
21
+ //# sourceMappingURL=empty-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-section.js","names":["category: RuleCategory","emptySectionRule: Rule"],"sources":["../../../src/checker/rules/empty-section.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\n\nconst category: RuleCategory = \"content\";\n\n/**\n * Check for section headings that have no content\n *\n * Note: This rule is a placeholder. Detecting empty sections requires access to\n * section boundaries in the AST, which isn't currently available.\n */\nexport const emptySectionRule: Rule = {\n code: \"empty-section\",\n name: \"Empty Section\",\n description: \"Section heading exists but has no content\",\n category,\n defaultSeverity: \"warning\",\n dependencies: { scope: \"entry\" },\n visitor: {},\n};\n"],"mappings":";AAEA,MAAMA,WAAyB;;;;;;;AAQ/B,MAAaC,mBAAyB;CACpC,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC,SAAS,EAAE;CACZ"}
@@ -0,0 +1,56 @@
1
+ //#region src/checker/rules/invalid-date-range-value.ts
2
+ const category = "metadata";
3
+ const dateRangePattern = /^\d{4}(-\d{2}(-\d{2})?)?\s*~\s*\d{4}(-\d{2}(-\d{2})?)?$/;
4
+ /**
5
+ * Check if a type expression is strictly a daterange type (not a union containing daterange).
6
+ * Union types like `datetime | daterange` are handled by the invalid-field-type rule
7
+ * which correctly validates that the value matches ANY member of the union.
8
+ */
9
+ function isStrictDateRangeType(type) {
10
+ return type.kind === "primitive" && type.name === "daterange";
11
+ }
12
+ const visitor = { visitInstanceEntry(entry, ctx) {
13
+ const registry = ctx.workspace.schemaRegistry;
14
+ const entity = entry.header.entity;
15
+ const schema = registry.get(entity);
16
+ if (!schema) return;
17
+ for (const meta of entry.metadata) {
18
+ const fieldName = meta.key.value;
19
+ const fieldSchema = schema.fields.get(fieldName);
20
+ if (!fieldSchema) continue;
21
+ if (!isStrictDateRangeType(fieldSchema.type)) continue;
22
+ const content = meta.value.content;
23
+ if (content.type === "daterange") continue;
24
+ let rangeValue = meta.value.raw.trim();
25
+ if (content.type === "quoted_value") rangeValue = content.value;
26
+ if (rangeValue && !dateRangePattern.test(rangeValue)) ctx.report({
27
+ message: `Invalid date range format '${rangeValue}' for field '${fieldName}'. Expected 'DATE ~ DATE' where DATE is YYYY, YYYY-MM, or YYYY-MM-DD.`,
28
+ file: ctx.file,
29
+ location: meta.value.location,
30
+ sourceMap: ctx.sourceMap,
31
+ data: {
32
+ fieldName,
33
+ value: rangeValue
34
+ }
35
+ });
36
+ }
37
+ } };
38
+ /**
39
+ * Check that date-range field values match the expected format (DATE ~ DATE)
40
+ */
41
+ const invalidDateRangeValueRule = {
42
+ code: "invalid-date-range-value",
43
+ name: "Invalid Date Range Value",
44
+ description: "Date range doesn't match DATE ~ DATE format",
45
+ category,
46
+ defaultSeverity: "error",
47
+ dependencies: {
48
+ scope: "entry",
49
+ schemas: true
50
+ },
51
+ visitor
52
+ };
53
+
54
+ //#endregion
55
+ export { invalidDateRangeValueRule };
56
+ //# sourceMappingURL=invalid-date-range-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-date-range-value.js","names":["category: RuleCategory","visitor: RuleVisitor","invalidDateRangeValueRule: Rule"],"sources":["../../../src/checker/rules/invalid-date-range-value.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport type { ModelTypeExpression } from \"../../model/workspace.js\";\n\nconst category: RuleCategory = \"metadata\";\n\n// Date range format: DATE ~ DATE where DATE is YYYY, YYYY-MM, or YYYY-MM-DD\nconst dateRangePattern = /^\\d{4}(-\\d{2}(-\\d{2})?)?\\s*~\\s*\\d{4}(-\\d{2}(-\\d{2})?)?$/;\n\n/**\n * Check if a type expression is strictly a daterange type (not a union containing daterange).\n * Union types like `datetime | daterange` are handled by the invalid-field-type rule\n * which correctly validates that the value matches ANY member of the union.\n */\nfunction isStrictDateRangeType(type: ModelTypeExpression): boolean {\n return type.kind === \"primitive\" && type.name === \"daterange\";\n}\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const registry = ctx.workspace.schemaRegistry;\n const entity = entry.header.entity;\n const schema = registry.get(entity);\n\n if (!schema) {\n return; // Will be caught by unknown-entity rule\n }\n\n for (const meta of entry.metadata) {\n const fieldName = meta.key.value;\n const fieldSchema = schema.fields.get(fieldName);\n\n if (!fieldSchema) {\n continue; // Will be caught by unknown-field rule\n }\n\n // Check if this field expects a strict date-range type (not a union)\n if (!isStrictDateRangeType(fieldSchema.type)) {\n continue;\n }\n\n // Validate the daterange format\n const content = meta.value.content;\n\n // For daterange content type, it's already validated by the parser\n if (content.type === \"daterange\") {\n continue;\n }\n\n // For quoted values, extract and validate\n let rangeValue = meta.value.raw.trim();\n if (content.type === \"quoted_value\") {\n rangeValue = content.value;\n }\n\n if (rangeValue && !dateRangePattern.test(rangeValue)) {\n ctx.report({\n message: `Invalid date range format '${rangeValue}' for field '${fieldName}'. Expected 'DATE ~ DATE' where DATE is YYYY, YYYY-MM, or YYYY-MM-DD.`,\n file: ctx.file,\n location: meta.value.location,\n sourceMap: ctx.sourceMap,\n data: { fieldName, value: rangeValue },\n });\n }\n }\n },\n};\n\n/**\n * Check that date-range field values match the expected format (DATE ~ DATE)\n */\nexport const invalidDateRangeValueRule: Rule = {\n code: \"invalid-date-range-value\",\n name: \"Invalid Date Range Value\",\n description: \"Date range doesn't match DATE ~ DATE format\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;AAG/B,MAAM,mBAAmB;;;;;;AAOzB,SAAS,sBAAsB,MAAoC;AACjE,QAAO,KAAK,SAAS,eAAe,KAAK,SAAS;;AAGpD,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;CAC7B,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,SAAS,MAAM,OAAO;CAC5B,MAAM,SAAS,SAAS,IAAI,OAAO;AAEnC,KAAI,CAAC,OACH;AAGF,MAAK,MAAM,QAAQ,MAAM,UAAU;EACjC,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,cAAc,OAAO,OAAO,IAAI,UAAU;AAEhD,MAAI,CAAC,YACH;AAIF,MAAI,CAAC,sBAAsB,YAAY,KAAK,CAC1C;EAIF,MAAM,UAAU,KAAK,MAAM;AAG3B,MAAI,QAAQ,SAAS,YACnB;EAIF,IAAI,aAAa,KAAK,MAAM,IAAI,MAAM;AACtC,MAAI,QAAQ,SAAS,eACnB,cAAa,QAAQ;AAGvB,MAAI,cAAc,CAAC,iBAAiB,KAAK,WAAW,CAClD,KAAI,OAAO;GACT,SAAS,8BAA8B,WAAW,eAAe,UAAU;GAC3E,MAAM,IAAI;GACV,UAAU,KAAK,MAAM;GACrB,WAAW,IAAI;GACf,MAAM;IAAE;IAAW,OAAO;IAAY;GACvC,CAAC;;GAIT;;;;AAKD,MAAaC,4BAAkC;CAC7C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,86 @@
1
+ import { isSyntaxError } from "../../ast/ast-types.js";
2
+ import { TypeExpr } from "../../schema/registry.js";
3
+
4
+ //#region src/checker/rules/invalid-default-value.ts
5
+ const category = "schema";
6
+ const visitor = { visitSchemaEntry(entry, ctx) {
7
+ const fields = entry.metadataBlock?.fields ?? [];
8
+ for (const field of fields) {
9
+ if (!field.defaultValue) continue;
10
+ if (isSyntaxError(field.typeExpr)) continue;
11
+ const defaultValue = convertDefaultValue(field.defaultValue);
12
+ const fieldType = convertTypeExpression(field.typeExpr);
13
+ if (!TypeExpr.matchesDefaultValue(defaultValue, fieldType)) ctx.report({
14
+ message: `Invalid default value '${field.defaultValue.raw}' for field '${field.name.value}'. Expected ${TypeExpr.toString(fieldType)}.`,
15
+ file: ctx.file,
16
+ location: field.defaultValue.location,
17
+ sourceMap: ctx.sourceMap,
18
+ data: {
19
+ fieldName: field.name.value,
20
+ defaultValue: field.defaultValue.raw,
21
+ expectedType: TypeExpr.toString(fieldType)
22
+ }
23
+ });
24
+ }
25
+ } };
26
+ /**
27
+ * Check that default values in field definitions match their declared types
28
+ */
29
+ const invalidDefaultValueRule = {
30
+ code: "invalid-default-value",
31
+ name: "Invalid Default Value",
32
+ description: "Default value doesn't match field's declared type",
33
+ category,
34
+ defaultSeverity: "error",
35
+ dependencies: { scope: "entry" },
36
+ visitor
37
+ };
38
+ function convertDefaultValue(dv) {
39
+ const raw = dv.raw;
40
+ switch (dv.content.type) {
41
+ case "quoted_value": return {
42
+ kind: "quoted",
43
+ value: dv.content.value,
44
+ raw
45
+ };
46
+ case "link": return {
47
+ kind: "link",
48
+ id: dv.content.id,
49
+ raw
50
+ };
51
+ case "datetime_value": return {
52
+ kind: "datetime",
53
+ value: dv.content.value,
54
+ raw
55
+ };
56
+ case "number_value": return {
57
+ kind: "number",
58
+ value: dv.content.value,
59
+ raw
60
+ };
61
+ }
62
+ }
63
+ function convertTypeExpression(expr) {
64
+ switch (expr.type) {
65
+ case "primitive_type": return {
66
+ kind: "primitive",
67
+ name: expr.name
68
+ };
69
+ case "literal_type": return {
70
+ kind: "literal",
71
+ value: expr.value
72
+ };
73
+ case "array_type": return {
74
+ kind: "array",
75
+ elementType: convertTypeExpression(expr.elementType)
76
+ };
77
+ case "union_type": return {
78
+ kind: "union",
79
+ members: expr.members.map((m) => convertTypeExpression(m))
80
+ };
81
+ }
82
+ }
83
+
84
+ //#endregion
85
+ export { invalidDefaultValueRule };
86
+ //# sourceMappingURL=invalid-default-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-default-value.js","names":["category: RuleCategory","visitor: RuleVisitor","invalidDefaultValueRule: Rule"],"sources":["../../../src/checker/rules/invalid-default-value.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport type { DefaultValue, TypeExpression } from \"../../ast/ast-types.js\";\nimport { isSyntaxError } from \"../../ast/ast-types.js\";\nimport type { ModelDefaultValue, ModelTypeExpression } from \"../../model/workspace.js\";\nimport { TypeExpr } from \"../../schema/registry.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n visitSchemaEntry(entry, ctx) {\n const fields = entry.metadataBlock?.fields ?? [];\n\n for (const field of fields) {\n if (!field.defaultValue) {\n continue;\n }\n\n // Skip fields with unknown types - they'll be reported as syntax errors\n if (isSyntaxError(field.typeExpr)) {\n continue;\n }\n\n // Convert AST default value to model default value format for type checking\n const defaultValue = convertDefaultValue(field.defaultValue);\n const fieldType = convertTypeExpression(field.typeExpr);\n\n // Use typed default value matching\n if (!TypeExpr.matchesDefaultValue(defaultValue, fieldType)) {\n ctx.report({\n message: `Invalid default value '${field.defaultValue.raw}' for field '${field.name.value}'. Expected ${TypeExpr.toString(fieldType)}.`,\n file: ctx.file,\n location: field.defaultValue.location,\n sourceMap: ctx.sourceMap,\n data: {\n fieldName: field.name.value,\n defaultValue: field.defaultValue.raw,\n expectedType: TypeExpr.toString(fieldType),\n },\n });\n }\n }\n },\n};\n\n/**\n * Check that default values in field definitions match their declared types\n */\nexport const invalidDefaultValueRule: Rule = {\n code: \"invalid-default-value\",\n name: \"Invalid Default Value\",\n description: \"Default value doesn't match field's declared type\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n\nfunction convertDefaultValue(dv: DefaultValue): ModelDefaultValue {\n const raw = dv.raw;\n switch (dv.content.type) {\n case \"quoted_value\":\n return { kind: \"quoted\", value: dv.content.value, raw };\n case \"link\":\n return { kind: \"link\", id: dv.content.id, raw };\n case \"datetime_value\":\n return { kind: \"datetime\", value: dv.content.value, raw };\n case \"number_value\":\n return { kind: \"number\", value: dv.content.value, raw };\n }\n}\n\nfunction convertTypeExpression(expr: TypeExpression): ModelTypeExpression {\n switch (expr.type) {\n case \"primitive_type\":\n return { kind: \"primitive\", name: expr.name };\n case \"literal_type\":\n return { kind: \"literal\", value: expr.value };\n case \"array_type\":\n // Safe: array element types cannot be arrays or unions per grammar\n return {\n kind: \"array\",\n elementType: convertTypeExpression(expr.elementType) as Exclude<\n ModelTypeExpression,\n { kind: \"array\" | \"union\" }\n >,\n };\n case \"union_type\":\n // Safe: union members cannot be unions per grammar\n return {\n kind: \"union\",\n members: expr.members.map(\n (m) => convertTypeExpression(m) as Exclude<ModelTypeExpression, { kind: \"union\" }>,\n ),\n };\n }\n}\n"],"mappings":";;;;AAOA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;CAC3B,MAAM,SAAS,MAAM,eAAe,UAAU,EAAE;AAEhD,MAAK,MAAM,SAAS,QAAQ;AAC1B,MAAI,CAAC,MAAM,aACT;AAIF,MAAI,cAAc,MAAM,SAAS,CAC/B;EAIF,MAAM,eAAe,oBAAoB,MAAM,aAAa;EAC5D,MAAM,YAAY,sBAAsB,MAAM,SAAS;AAGvD,MAAI,CAAC,SAAS,oBAAoB,cAAc,UAAU,CACxD,KAAI,OAAO;GACT,SAAS,0BAA0B,MAAM,aAAa,IAAI,eAAe,MAAM,KAAK,MAAM,cAAc,SAAS,SAAS,UAAU,CAAC;GACrI,MAAM,IAAI;GACV,UAAU,MAAM,aAAa;GAC7B,WAAW,IAAI;GACf,MAAM;IACJ,WAAW,MAAM,KAAK;IACtB,cAAc,MAAM,aAAa;IACjC,cAAc,SAAS,SAAS,UAAU;IAC3C;GACF,CAAC;;GAIT;;;;AAKD,MAAaC,0BAAgC;CAC3C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD;AAED,SAAS,oBAAoB,IAAqC;CAChE,MAAM,MAAM,GAAG;AACf,SAAQ,GAAG,QAAQ,MAAnB;EACE,KAAK,eACH,QAAO;GAAE,MAAM;GAAU,OAAO,GAAG,QAAQ;GAAO;GAAK;EACzD,KAAK,OACH,QAAO;GAAE,MAAM;GAAQ,IAAI,GAAG,QAAQ;GAAI;GAAK;EACjD,KAAK,iBACH,QAAO;GAAE,MAAM;GAAY,OAAO,GAAG,QAAQ;GAAO;GAAK;EAC3D,KAAK,eACH,QAAO;GAAE,MAAM;GAAU,OAAO,GAAG,QAAQ;GAAO;GAAK;;;AAI7D,SAAS,sBAAsB,MAA2C;AACxE,SAAQ,KAAK,MAAb;EACE,KAAK,iBACH,QAAO;GAAE,MAAM;GAAa,MAAM,KAAK;GAAM;EAC/C,KAAK,eACH,QAAO;GAAE,MAAM;GAAW,OAAO,KAAK;GAAO;EAC/C,KAAK,aAEH,QAAO;GACL,MAAM;GACN,aAAa,sBAAsB,KAAK,YAAY;GAIrD;EACH,KAAK,aAEH,QAAO;GACL,MAAM;GACN,SAAS,KAAK,QAAQ,KACnB,MAAM,sBAAsB,EAAE,CAChC;GACF"}
@@ -0,0 +1,45 @@
1
+ import { TypeExpr } from "../../schema/registry.js";
2
+
3
+ //#region src/checker/rules/invalid-field-type.ts
4
+ const category = "instance";
5
+ const visitor = { visitInstanceEntry(entry, ctx) {
6
+ const registry = ctx.workspace.schemaRegistry;
7
+ const entity = entry.header.entity;
8
+ const schema = registry.get(entity);
9
+ if (!schema) return;
10
+ for (const meta of entry.metadata) {
11
+ const fieldName = meta.key.value;
12
+ const fieldSchema = schema.fields.get(fieldName);
13
+ if (!fieldSchema) continue;
14
+ if (!TypeExpr.matchesContent(meta.value.content, fieldSchema.type)) ctx.report({
15
+ message: `Invalid value '${meta.value.raw}' for field '${fieldName}'. Expected ${TypeExpr.toString(fieldSchema.type)}.`,
16
+ file: ctx.file,
17
+ location: meta.value.location,
18
+ sourceMap: ctx.sourceMap,
19
+ data: {
20
+ field: fieldName,
21
+ value: meta.value.raw,
22
+ expectedType: TypeExpr.toString(fieldSchema.type)
23
+ }
24
+ });
25
+ }
26
+ } };
27
+ /**
28
+ * Check that metadata values match their declared types
29
+ */
30
+ const invalidFieldTypeRule = {
31
+ code: "invalid-field-type",
32
+ name: "Invalid Field Type",
33
+ description: "Metadata value doesn't match declared type",
34
+ category,
35
+ defaultSeverity: "error",
36
+ dependencies: {
37
+ scope: "entry",
38
+ schemas: true
39
+ },
40
+ visitor
41
+ };
42
+
43
+ //#endregion
44
+ export { invalidFieldTypeRule };
45
+ //# sourceMappingURL=invalid-field-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-field-type.js","names":["category: RuleCategory","visitor: RuleVisitor","invalidFieldTypeRule: Rule"],"sources":["../../../src/checker/rules/invalid-field-type.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport { TypeExpr } from \"../../schema/registry.js\";\n\nconst category: RuleCategory = \"instance\";\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const registry = ctx.workspace.schemaRegistry;\n const entity = entry.header.entity;\n const schema = registry.get(entity);\n\n if (!schema) {\n return; // Will be caught by unknown-entity rule\n }\n\n for (const meta of entry.metadata) {\n const fieldName = meta.key.value;\n const fieldSchema = schema.fields.get(fieldName);\n\n if (!fieldSchema) {\n continue; // Will be caught by unknown-field rule\n }\n\n // Check if value matches the type using grammar-parsed content\n if (!TypeExpr.matchesContent(meta.value.content, fieldSchema.type)) {\n ctx.report({\n message: `Invalid value '${meta.value.raw}' for field '${fieldName}'. Expected ${TypeExpr.toString(fieldSchema.type)}.`,\n file: ctx.file,\n location: meta.value.location,\n sourceMap: ctx.sourceMap,\n data: {\n field: fieldName,\n value: meta.value.raw,\n expectedType: TypeExpr.toString(fieldSchema.type),\n },\n });\n }\n }\n },\n};\n\n/**\n * Check that metadata values match their declared types\n */\nexport const invalidFieldTypeRule: Rule = {\n code: \"invalid-field-type\",\n name: \"Invalid Field Type\",\n description: \"Metadata value doesn't match declared type\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";;;AAIA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;CAC7B,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,SAAS,MAAM,OAAO;CAC5B,MAAM,SAAS,SAAS,IAAI,OAAO;AAEnC,KAAI,CAAC,OACH;AAGF,MAAK,MAAM,QAAQ,MAAM,UAAU;EACjC,MAAM,YAAY,KAAK,IAAI;EAC3B,MAAM,cAAc,OAAO,OAAO,IAAI,UAAU;AAEhD,MAAI,CAAC,YACH;AAIF,MAAI,CAAC,SAAS,eAAe,KAAK,MAAM,SAAS,YAAY,KAAK,CAChE,KAAI,OAAO;GACT,SAAS,kBAAkB,KAAK,MAAM,IAAI,eAAe,UAAU,cAAc,SAAS,SAAS,YAAY,KAAK,CAAC;GACrH,MAAM,IAAI;GACV,UAAU,KAAK,MAAM;GACrB,WAAW,IAAI;GACf,MAAM;IACJ,OAAO;IACP,OAAO,KAAK,MAAM;IAClB,cAAc,SAAS,SAAS,YAAY,KAAK;IAClD;GACF,CAAC;;GAIT;;;;AAKD,MAAaC,uBAA6B;CACxC,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,48 @@
1
+ //#region src/checker/rules/missing-required-field.ts
2
+ const category = "instance";
3
+ /**
4
+ * Get metadata field names from entry
5
+ */
6
+ function getMetadataFieldNames(entry) {
7
+ return new Set(entry.metadata.map((m) => m.key.value));
8
+ }
9
+ const visitor = { visitInstanceEntry(entry, ctx) {
10
+ const registry = ctx.workspace.schemaRegistry;
11
+ const entity = entry.header.entity;
12
+ const schema = registry.get(entity);
13
+ if (!schema) return;
14
+ const presentFields = getMetadataFieldNames(entry);
15
+ for (const [fieldName, fieldSchema] of schema.fields) {
16
+ if (fieldSchema.optional) continue;
17
+ if (fieldSchema.defaultValue !== null) continue;
18
+ if (!presentFields.has(fieldName)) ctx.report({
19
+ message: `Missing required field '${fieldName}' for entity '${entity}'.`,
20
+ file: ctx.file,
21
+ location: entry.location,
22
+ sourceMap: ctx.sourceMap,
23
+ data: {
24
+ field: fieldName,
25
+ entity
26
+ }
27
+ });
28
+ }
29
+ } };
30
+ /**
31
+ * Check for missing required metadata fields in instance entries
32
+ */
33
+ const missingRequiredFieldRule = {
34
+ code: "missing-required-field",
35
+ name: "Missing Required Field",
36
+ description: "Required metadata field not present",
37
+ category,
38
+ defaultSeverity: "error",
39
+ dependencies: {
40
+ scope: "entry",
41
+ schemas: true
42
+ },
43
+ visitor
44
+ };
45
+
46
+ //#endregion
47
+ export { missingRequiredFieldRule };
48
+ //# sourceMappingURL=missing-required-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-required-field.js","names":["category: RuleCategory","visitor: RuleVisitor","missingRequiredFieldRule: Rule"],"sources":["../../../src/checker/rules/missing-required-field.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport type { InstanceEntry } from \"../../ast/ast-types.js\";\n\nconst category: RuleCategory = \"instance\";\n\n/**\n * Get metadata field names from entry\n */\nfunction getMetadataFieldNames(entry: InstanceEntry): Set<string> {\n return new Set(entry.metadata.map((m) => m.key.value));\n}\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const registry = ctx.workspace.schemaRegistry;\n const entity = entry.header.entity;\n const schema = registry.get(entity);\n\n if (!schema) {\n return; // Will be caught by unknown-entity rule\n }\n\n const presentFields = getMetadataFieldNames(entry);\n\n for (const [fieldName, fieldSchema] of schema.fields) {\n if (fieldSchema.optional) {\n continue;\n }\n if (fieldSchema.defaultValue !== null) {\n continue; // Has default, not required\n }\n\n if (!presentFields.has(fieldName)) {\n ctx.report({\n message: `Missing required field '${fieldName}' for entity '${entity}'.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { field: fieldName, entity },\n });\n }\n }\n },\n};\n\n/**\n * Check for missing required metadata fields in instance entries\n */\nexport const missingRequiredFieldRule: Rule = {\n code: \"missing-required-field\",\n name: \"Missing Required Field\",\n description: \"Required metadata field not present\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;;;;AAK/B,SAAS,sBAAsB,OAAmC;AAChE,QAAO,IAAI,IAAI,MAAM,SAAS,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC;;AAGxD,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;CAC7B,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,SAAS,MAAM,OAAO;CAC5B,MAAM,SAAS,SAAS,IAAI,OAAO;AAEnC,KAAI,CAAC,OACH;CAGF,MAAM,gBAAgB,sBAAsB,MAAM;AAElD,MAAK,MAAM,CAAC,WAAW,gBAAgB,OAAO,QAAQ;AACpD,MAAI,YAAY,SACd;AAEF,MAAI,YAAY,iBAAiB,KAC/B;AAGF,MAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,KAAI,OAAO;GACT,SAAS,2BAA2B,UAAU,gBAAgB,OAAO;GACrE,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IAAE,OAAO;IAAW;IAAQ;GACnC,CAAC;;GAIT;;;;AAKD,MAAaC,2BAAiC;CAC5C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,51 @@
1
+ //#region src/checker/rules/missing-required-section.ts
2
+ const category = "instance";
3
+ /**
4
+ * Extract section names from entry content (markdown headers)
5
+ */
6
+ function getSectionNames(entry) {
7
+ if (!entry.content) return [];
8
+ return entry.content.children.filter((c) => c.type === "markdown_header").map((h) => {
9
+ const match = h.text.match(/^#+\s*(.+)$/);
10
+ return match ? match[1].trim() : h.text;
11
+ });
12
+ }
13
+ const visitor = { visitInstanceEntry(entry, ctx) {
14
+ const registry = ctx.workspace.schemaRegistry;
15
+ const entity = entry.header.entity;
16
+ const schema = registry.get(entity);
17
+ if (!schema) return;
18
+ const presentSections = getSectionNames(entry);
19
+ for (const [sectionName, sectionSchema] of schema.sections) {
20
+ if (sectionSchema.optional) continue;
21
+ if (!presentSections.includes(sectionName)) ctx.report({
22
+ message: `Missing required section '${sectionName}' for entity '${entity}'.`,
23
+ file: ctx.file,
24
+ location: entry.location,
25
+ sourceMap: ctx.sourceMap,
26
+ data: {
27
+ section: sectionName,
28
+ entity
29
+ }
30
+ });
31
+ }
32
+ } };
33
+ /**
34
+ * Check for missing required sections in instance entry content
35
+ */
36
+ const missingRequiredSectionRule = {
37
+ code: "missing-required-section",
38
+ name: "Missing Required Section",
39
+ description: "Required section not present in content",
40
+ category,
41
+ defaultSeverity: "error",
42
+ dependencies: {
43
+ scope: "entry",
44
+ schemas: true
45
+ },
46
+ visitor
47
+ };
48
+
49
+ //#endregion
50
+ export { missingRequiredSectionRule };
51
+ //# sourceMappingURL=missing-required-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-required-section.js","names":["category: RuleCategory","visitor: RuleVisitor","missingRequiredSectionRule: Rule"],"sources":["../../../src/checker/rules/missing-required-section.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport type { InstanceEntry } from \"../../ast/ast-types.js\";\n\nconst category: RuleCategory = \"instance\";\n\n/**\n * Extract section names from entry content (markdown headers)\n */\nfunction getSectionNames(entry: InstanceEntry): string[] {\n if (!entry.content) {\n return [];\n }\n return entry.content.children\n .filter((c) => c.type === \"markdown_header\")\n .map((h) => {\n // Extract section name from \"# SectionName\" format\n const match = h.text.match(/^#+\\s*(.+)$/);\n return match ? match[1].trim() : h.text;\n });\n}\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const registry = ctx.workspace.schemaRegistry;\n const entity = entry.header.entity;\n const schema = registry.get(entity);\n\n if (!schema) {\n return; // Will be caught by unknown-entity rule\n }\n\n const presentSections = getSectionNames(entry);\n\n for (const [sectionName, sectionSchema] of schema.sections) {\n if (sectionSchema.optional) {\n continue;\n }\n\n if (!presentSections.includes(sectionName)) {\n ctx.report({\n message: `Missing required section '${sectionName}' for entity '${entity}'.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { section: sectionName, entity },\n });\n }\n }\n },\n};\n\n/**\n * Check for missing required sections in instance entry content\n */\nexport const missingRequiredSectionRule: Rule = {\n code: \"missing-required-section\",\n name: \"Missing Required Section\",\n description: \"Required section not present in content\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;;;;AAK/B,SAAS,gBAAgB,OAAgC;AACvD,KAAI,CAAC,MAAM,QACT,QAAO,EAAE;AAEX,QAAO,MAAM,QAAQ,SAClB,QAAQ,MAAM,EAAE,SAAS,kBAAkB,CAC3C,KAAK,MAAM;EAEV,MAAM,QAAQ,EAAE,KAAK,MAAM,cAAc;AACzC,SAAO,QAAQ,MAAM,GAAG,MAAM,GAAG,EAAE;GACnC;;AAGN,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;CAC7B,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,SAAS,MAAM,OAAO;CAC5B,MAAM,SAAS,SAAS,IAAI,OAAO;AAEnC,KAAI,CAAC,OACH;CAGF,MAAM,kBAAkB,gBAAgB,MAAM;AAE9C,MAAK,MAAM,CAAC,aAAa,kBAAkB,OAAO,UAAU;AAC1D,MAAI,cAAc,SAChB;AAGF,MAAI,CAAC,gBAAgB,SAAS,YAAY,CACxC,KAAI,OAAO;GACT,SAAS,6BAA6B,YAAY,gBAAgB,OAAO;GACzE,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IAAE,SAAS;IAAa;IAAQ;GACvC,CAAC;;GAIT;;;;AAKD,MAAaC,6BAAmC;CAC9C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,56 @@
1
+ //#region src/checker/rules/missing-title.ts
2
+ const category = "instance";
3
+ const visitor = {
4
+ visitInstanceEntry(entry, ctx) {
5
+ const title = entry.header.title?.value;
6
+ if (!title || title.trim() === "") ctx.report({
7
+ message: `Entry is missing a title. Provide a descriptive title in quotes.`,
8
+ file: ctx.file,
9
+ location: entry.location,
10
+ sourceMap: ctx.sourceMap,
11
+ data: {
12
+ directive: entry.header.directive,
13
+ entity: entry.header.entity
14
+ }
15
+ });
16
+ },
17
+ visitSchemaEntry(entry, ctx) {
18
+ const title = entry.header.title?.value;
19
+ if (!title || title.trim() === "") ctx.report({
20
+ message: `Schema entry is missing a title/description. Provide a description in quotes.`,
21
+ file: ctx.file,
22
+ location: entry.location,
23
+ sourceMap: ctx.sourceMap,
24
+ data: {
25
+ directive: entry.header.directive,
26
+ entityName: entry.header.entityName.value
27
+ }
28
+ });
29
+ },
30
+ visitSynthesisEntry(entry, ctx) {
31
+ const title = entry.header.title?.value;
32
+ if (!title || title.trim() === "") ctx.report({
33
+ message: `Synthesis entry is missing a title. Provide a descriptive title in quotes.`,
34
+ file: ctx.file,
35
+ location: entry.location,
36
+ sourceMap: ctx.sourceMap,
37
+ data: { linkId: entry.header.linkId.id }
38
+ });
39
+ }
40
+ };
41
+ /**
42
+ * Check for entries with empty titles
43
+ */
44
+ const missingTitleRule = {
45
+ code: "missing-title",
46
+ name: "Missing Title",
47
+ description: "Entry has empty or missing title",
48
+ category,
49
+ defaultSeverity: "error",
50
+ dependencies: { scope: "entry" },
51
+ visitor
52
+ };
53
+
54
+ //#endregion
55
+ export { missingTitleRule };
56
+ //# sourceMappingURL=missing-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-title.js","names":["category: RuleCategory","visitor: RuleVisitor","missingTitleRule: Rule"],"sources":["../../../src/checker/rules/missing-title.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\n\nconst category: RuleCategory = \"instance\";\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const title = entry.header.title?.value;\n if (!title || title.trim() === \"\") {\n ctx.report({\n message: `Entry is missing a title. Provide a descriptive title in quotes.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { directive: entry.header.directive, entity: entry.header.entity },\n });\n }\n },\n\n visitSchemaEntry(entry, ctx) {\n const title = entry.header.title?.value;\n if (!title || title.trim() === \"\") {\n ctx.report({\n message: `Schema entry is missing a title/description. Provide a description in quotes.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: {\n directive: entry.header.directive,\n entityName: entry.header.entityName.value,\n },\n });\n }\n },\n\n visitSynthesisEntry(entry, ctx) {\n const title = entry.header.title?.value;\n if (!title || title.trim() === \"\") {\n ctx.report({\n message: `Synthesis entry is missing a title. Provide a descriptive title in quotes.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { linkId: entry.header.linkId.id },\n });\n }\n },\n};\n\n/**\n * Check for entries with empty titles\n */\nexport const missingTitleRule: Rule = {\n code: \"missing-title\",\n name: \"Missing Title\",\n description: \"Entry has empty or missing title\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB;CAC3B,mBAAmB,OAAO,KAAK;EAC7B,MAAM,QAAQ,MAAM,OAAO,OAAO;AAClC,MAAI,CAAC,SAAS,MAAM,MAAM,KAAK,GAC7B,KAAI,OAAO;GACT,SAAS;GACT,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IAAE,WAAW,MAAM,OAAO;IAAW,QAAQ,MAAM,OAAO;IAAQ;GACzE,CAAC;;CAIN,iBAAiB,OAAO,KAAK;EAC3B,MAAM,QAAQ,MAAM,OAAO,OAAO;AAClC,MAAI,CAAC,SAAS,MAAM,MAAM,KAAK,GAC7B,KAAI,OAAO;GACT,SAAS;GACT,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IACJ,WAAW,MAAM,OAAO;IACxB,YAAY,MAAM,OAAO,WAAW;IACrC;GACF,CAAC;;CAIN,oBAAoB,OAAO,KAAK;EAC9B,MAAM,QAAQ,MAAM,OAAO,OAAO;AAClC,MAAI,CAAC,SAAS,MAAM,MAAM,KAAK,GAC7B,KAAI,OAAO;GACT,SAAS;GACT,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,IAAI;GACzC,CAAC;;CAGP;;;;AAKD,MAAaC,mBAAyB;CACpC,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,42 @@
1
+ //#region src/checker/rules/remove-undefined-field.ts
2
+ const category = "schema";
3
+ const visitor = { visitSchemaEntry(entry, ctx) {
4
+ if (entry.header.directive !== "alter-entity") return;
5
+ const removeFields = entry.removeMetadataBlock?.fields ?? [];
6
+ if (removeFields.length === 0) return;
7
+ const entityName = entry.header.entityName.value;
8
+ const schema = ctx.workspace.schemaRegistry.get(entityName);
9
+ if (!schema) return;
10
+ for (const removal of removeFields) {
11
+ const fieldName = removal.name.value;
12
+ if (!schema.fields.has(fieldName)) ctx.report({
13
+ message: `Cannot remove field '${fieldName}' from entity '${entityName}': field does not exist.`,
14
+ file: ctx.file,
15
+ location: removal.location,
16
+ sourceMap: ctx.sourceMap,
17
+ data: {
18
+ fieldName,
19
+ entityName
20
+ }
21
+ });
22
+ }
23
+ } };
24
+ /**
25
+ * Check for alter-entity trying to remove fields that don't exist in the schema
26
+ */
27
+ const removeUndefinedFieldRule = {
28
+ code: "remove-undefined-field",
29
+ name: "Remove Undefined Field",
30
+ description: "# Remove Metadata references nonexistent field",
31
+ category,
32
+ defaultSeverity: "warning",
33
+ dependencies: {
34
+ scope: "entry",
35
+ schemas: true
36
+ },
37
+ visitor
38
+ };
39
+
40
+ //#endregion
41
+ export { removeUndefinedFieldRule };
42
+ //# sourceMappingURL=remove-undefined-field.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-undefined-field.js","names":["category: RuleCategory","visitor: RuleVisitor","removeUndefinedFieldRule: Rule"],"sources":["../../../src/checker/rules/remove-undefined-field.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n visitSchemaEntry(entry, ctx) {\n if (entry.header.directive !== \"alter-entity\") {\n return;\n }\n\n const removeFields = entry.removeMetadataBlock?.fields ?? [];\n if (removeFields.length === 0) {\n return;\n }\n\n const entityName = entry.header.entityName.value;\n const schema = ctx.workspace.schemaRegistry.get(entityName);\n\n if (!schema) {\n return; // Will be caught by alter-undefined-entity rule\n }\n\n for (const removal of removeFields) {\n const fieldName = removal.name.value;\n if (!schema.fields.has(fieldName)) {\n ctx.report({\n message: `Cannot remove field '${fieldName}' from entity '${entityName}': field does not exist.`,\n file: ctx.file,\n location: removal.location,\n sourceMap: ctx.sourceMap,\n data: { fieldName, entityName },\n });\n }\n }\n },\n};\n\n/**\n * Check for alter-entity trying to remove fields that don't exist in the schema\n */\nexport const removeUndefinedFieldRule: Rule = {\n code: \"remove-undefined-field\",\n name: \"Remove Undefined Field\",\n description: \"# Remove Metadata references nonexistent field\",\n category,\n defaultSeverity: \"warning\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;AAC3B,KAAI,MAAM,OAAO,cAAc,eAC7B;CAGF,MAAM,eAAe,MAAM,qBAAqB,UAAU,EAAE;AAC5D,KAAI,aAAa,WAAW,EAC1B;CAGF,MAAM,aAAa,MAAM,OAAO,WAAW;CAC3C,MAAM,SAAS,IAAI,UAAU,eAAe,IAAI,WAAW;AAE3D,KAAI,CAAC,OACH;AAGF,MAAK,MAAM,WAAW,cAAc;EAClC,MAAM,YAAY,QAAQ,KAAK;AAC/B,MAAI,CAAC,OAAO,OAAO,IAAI,UAAU,CAC/B,KAAI,OAAO;GACT,SAAS,wBAAwB,UAAU,iBAAiB,WAAW;GACvE,MAAM,IAAI;GACV,UAAU,QAAQ;GAClB,WAAW,IAAI;GACf,MAAM;IAAE;IAAW;IAAY;GAChC,CAAC;;GAIT;;;;AAKD,MAAaC,2BAAiC;CAC5C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,42 @@
1
+ //#region src/checker/rules/remove-undefined-section.ts
2
+ const category = "schema";
3
+ const visitor = { visitSchemaEntry(entry, ctx) {
4
+ if (entry.header.directive !== "alter-entity") return;
5
+ const removeSections = entry.removeSectionsBlock?.sections ?? [];
6
+ if (removeSections.length === 0) return;
7
+ const entityName = entry.header.entityName.value;
8
+ const schema = ctx.workspace.schemaRegistry.get(entityName);
9
+ if (!schema) return;
10
+ for (const removal of removeSections) {
11
+ const sectionName = removal.name.value;
12
+ if (!schema.sections.has(sectionName)) ctx.report({
13
+ message: `Cannot remove section '${sectionName}' from entity '${entityName}': section does not exist.`,
14
+ file: ctx.file,
15
+ location: removal.location,
16
+ sourceMap: ctx.sourceMap,
17
+ data: {
18
+ sectionName,
19
+ entityName
20
+ }
21
+ });
22
+ }
23
+ } };
24
+ /**
25
+ * Check for alter-entity trying to remove sections that don't exist in the schema
26
+ */
27
+ const removeUndefinedSectionRule = {
28
+ code: "remove-undefined-section",
29
+ name: "Remove Undefined Section",
30
+ description: "# Remove Sections references nonexistent section",
31
+ category,
32
+ defaultSeverity: "warning",
33
+ dependencies: {
34
+ scope: "entry",
35
+ schemas: true
36
+ },
37
+ visitor
38
+ };
39
+
40
+ //#endregion
41
+ export { removeUndefinedSectionRule };
42
+ //# sourceMappingURL=remove-undefined-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-undefined-section.js","names":["category: RuleCategory","visitor: RuleVisitor","removeUndefinedSectionRule: Rule"],"sources":["../../../src/checker/rules/remove-undefined-section.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n visitSchemaEntry(entry, ctx) {\n if (entry.header.directive !== \"alter-entity\") {\n return;\n }\n\n const removeSections = entry.removeSectionsBlock?.sections ?? [];\n if (removeSections.length === 0) {\n return;\n }\n\n const entityName = entry.header.entityName.value;\n const schema = ctx.workspace.schemaRegistry.get(entityName);\n\n if (!schema) {\n return; // Will be caught by alter-undefined-entity rule\n }\n\n for (const removal of removeSections) {\n const sectionName = removal.name.value;\n if (!schema.sections.has(sectionName)) {\n ctx.report({\n message: `Cannot remove section '${sectionName}' from entity '${entityName}': section does not exist.`,\n file: ctx.file,\n location: removal.location,\n sourceMap: ctx.sourceMap,\n data: { sectionName, entityName },\n });\n }\n }\n },\n};\n\n/**\n * Check for alter-entity trying to remove sections that don't exist in the schema\n */\nexport const removeUndefinedSectionRule: Rule = {\n code: \"remove-undefined-section\",\n name: \"Remove Undefined Section\",\n description: \"# Remove Sections references nonexistent section\",\n category,\n defaultSeverity: \"warning\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;AAC3B,KAAI,MAAM,OAAO,cAAc,eAC7B;CAGF,MAAM,iBAAiB,MAAM,qBAAqB,YAAY,EAAE;AAChE,KAAI,eAAe,WAAW,EAC5B;CAGF,MAAM,aAAa,MAAM,OAAO,WAAW;CAC3C,MAAM,SAAS,IAAI,UAAU,eAAe,IAAI,WAAW;AAE3D,KAAI,CAAC,OACH;AAGF,MAAK,MAAM,WAAW,gBAAgB;EACpC,MAAM,cAAc,QAAQ,KAAK;AACjC,MAAI,CAAC,OAAO,SAAS,IAAI,YAAY,CACnC,KAAI,OAAO;GACT,SAAS,0BAA0B,YAAY,iBAAiB,WAAW;GAC3E,MAAM,IAAI;GACV,UAAU,QAAQ;GAClB,WAAW,IAAI;GACf,MAAM;IAAE;IAAa;IAAY;GAClC,CAAC;;GAIT;;;;AAKD,MAAaC,6BAAmC;CAC9C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}
@@ -0,0 +1,71 @@
1
+ import { RuleVisitor } from "../visitor.js";
2
+
3
+ //#region src/checker/rules/rules.d.ts
4
+ /**
5
+ * Severity levels for diagnostics
6
+ */
7
+ type Severity = "error" | "warning" | "info" | "off";
8
+ /**
9
+ * Rule categories for grouping related rules
10
+ */
11
+ type RuleCategory = "instance" | "link" | "schema" | "metadata" | "content";
12
+ /**
13
+ * Display order and labels for rule categories
14
+ */
15
+ declare const RULE_CATEGORIES: Record<RuleCategory, {
16
+ order: number;
17
+ label: string;
18
+ }>;
19
+ /**
20
+ * Rule scope - determines when a rule needs to re-run.
21
+ *
22
+ * - "entry": Rule only looks at individual entries, can run incrementally on changed entries
23
+ * - "document": Rule looks at entries within a single document, runs on full document
24
+ * - "workspace": Rule looks across multiple documents, runs on full workspace
25
+ */
26
+ type RuleScope = "entry" | "document" | "workspace";
27
+ /**
28
+ * Dependency declaration for a rule.
29
+ * Used for incremental checking to determine when a rule needs to re-run.
30
+ */
31
+ interface RuleDependencies {
32
+ /** Rule scope - determines when rule needs to re-run */
33
+ scope: RuleScope;
34
+ /** Rule needs schema registry data */
35
+ schemas?: boolean;
36
+ /** Rule needs link index data */
37
+ links?: boolean;
38
+ /** Entity names this rule cares about (for targeted invalidation) */
39
+ entities?: "all" | string[];
40
+ }
41
+ /**
42
+ * A validation rule
43
+ */
44
+ interface Rule {
45
+ /** Unique rule code (e.g., "unknown-entity") */
46
+ code: string;
47
+ /** Human-readable rule name */
48
+ name: string;
49
+ /** Short description of what this rule checks */
50
+ description: string;
51
+ /** Category for grouping related rules */
52
+ category: RuleCategory;
53
+ /** Default severity for this rule */
54
+ defaultSeverity: Severity;
55
+ /**
56
+ * Dependency declaration for incremental checking.
57
+ */
58
+ dependencies: RuleDependencies;
59
+ /**
60
+ * Visitor-based implementation.
61
+ * Allows single-pass checking with other rules.
62
+ */
63
+ visitor: RuleVisitor;
64
+ }
65
+ /**
66
+ * All available validation rules
67
+ */
68
+ declare const allRules: Rule[];
69
+ //#endregion
70
+ export { RULE_CATEGORIES, Rule, RuleCategory, Severity, allRules };
71
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","names":[],"sources":["../../../src/checker/rules/rules.ts"],"sourcesContent":[],"mappings":";;;;;;AAGY,KAAA,QAAA,GAAQ,OAAA,GAAA,SAAA,GAAA,MAAA,GAAA,KAAA;AAKpB;AAKA;AAeA;AAMiB,KA1BL,YAAA,GA0BqB,UAExB,GAAA,MAAS,GAAA,QAAA,GAAA,UAAA,GAAA,SAAA;AAYlB;;;AAegB,cAlDH,eAkDG,EAlDc,MAkDd,CAlDqB,YAkDrB,EAAA;EAAgB,KAAA,EAAA,MAAA;EAMc,KAAA,EAAA,MAAA;AAqD9C,CAAA,CAAA;;;;;;;;KA9FY,SAAA;;;;;UAMK,gBAAA;;SAER;;;;;;;;;;;UAYQ,IAAA;;;;;;;;YAQL;;mBAEO;;;;gBAKH;;;;;WAAgB;;;;;cA2DnB,UAAU"}