@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,53 @@
1
+ import { formatTimestamp } from "../../formatters.js";
2
+
3
+ //#region src/checker/rules/alter-before-define.ts
4
+ const category = "schema";
5
+ const visitor = { afterCheck(ctx) {
6
+ const { index } = ctx;
7
+ for (const [entityName, alterEntries] of index.alterEntitiesByName) {
8
+ const defineEntries = index.defineEntitiesByName.get(entityName);
9
+ if (!defineEntries || defineEntries.length === 0) continue;
10
+ let earliestDefine = null;
11
+ let earliestDefineTs = "";
12
+ for (const entry of defineEntries) {
13
+ const ts = formatTimestamp(entry.entry.header.timestamp);
14
+ if (!earliestDefine || ts < earliestDefineTs) {
15
+ earliestDefine = entry;
16
+ earliestDefineTs = ts;
17
+ }
18
+ }
19
+ for (const { entry, file, sourceMap } of alterEntries) {
20
+ const alterTs = formatTimestamp(entry.header.timestamp);
21
+ if (alterTs < earliestDefineTs) ctx.report({
22
+ message: `alter-entity for '${entityName}' has timestamp ${alterTs} which is before the define-entity at ${earliestDefineTs}.`,
23
+ file,
24
+ location: entry.location,
25
+ sourceMap,
26
+ data: {
27
+ entityName,
28
+ alterTimestamp: alterTs,
29
+ defineTimestamp: earliestDefineTs
30
+ }
31
+ });
32
+ }
33
+ }
34
+ } };
35
+ /**
36
+ * Check for alter-entity entries with timestamps earlier than the define-entity
37
+ */
38
+ const alterBeforeDefineRule = {
39
+ code: "alter-before-define",
40
+ name: "Alter Before Define",
41
+ description: "alter-entity timestamp before define-entity",
42
+ category,
43
+ defaultSeverity: "error",
44
+ dependencies: {
45
+ scope: "workspace",
46
+ schemas: true
47
+ },
48
+ visitor
49
+ };
50
+
51
+ //#endregion
52
+ export { alterBeforeDefineRule };
53
+ //# sourceMappingURL=alter-before-define.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alter-before-define.js","names":["category: RuleCategory","visitor: RuleVisitor","earliestDefine: IndexedEntry<SchemaEntry> | null","alterBeforeDefineRule: Rule"],"sources":["../../../src/checker/rules/alter-before-define.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor, VisitorContext } from \"../visitor.js\";\nimport type { SchemaEntry } from \"../../ast/ast-types.js\";\nimport type { IndexedEntry } from \"../workspace-index.js\";\nimport { formatTimestamp } from \"../../formatters.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n afterCheck(ctx: VisitorContext) {\n const { index } = ctx;\n\n // For each entity with alter entries, check against define entries\n for (const [entityName, alterEntries] of index.alterEntitiesByName) {\n const defineEntries = index.defineEntitiesByName.get(entityName);\n if (!defineEntries || defineEntries.length === 0) {\n continue; // No define - handled by alter-undefined-entity rule\n }\n\n // Find the earliest define timestamp\n let earliestDefine: IndexedEntry<SchemaEntry> | null = null;\n let earliestDefineTs = \"\";\n\n for (const entry of defineEntries) {\n const ts = formatTimestamp(entry.entry.header.timestamp);\n if (!earliestDefine || ts < earliestDefineTs) {\n earliestDefine = entry;\n earliestDefineTs = ts;\n }\n }\n\n // Check each alter entry\n for (const { entry, file, sourceMap } of alterEntries) {\n const alterTs = formatTimestamp(entry.header.timestamp);\n\n if (alterTs < earliestDefineTs) {\n ctx.report({\n message: `alter-entity for '${entityName}' has timestamp ${alterTs} which is before the define-entity at ${earliestDefineTs}.`,\n file,\n location: entry.location,\n sourceMap,\n data: {\n entityName,\n alterTimestamp: alterTs,\n defineTimestamp: earliestDefineTs,\n },\n });\n }\n }\n }\n },\n};\n\n/**\n * Check for alter-entity entries with timestamps earlier than the define-entity\n */\nexport const alterBeforeDefineRule: Rule = {\n code: \"alter-before-define\",\n name: \"Alter Before Define\",\n description: \"alter-entity timestamp before define-entity\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"workspace\", schemas: true },\n visitor,\n};\n"],"mappings":";;;AAMA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,WAAW,KAAqB;CAC9B,MAAM,EAAE,UAAU;AAGlB,MAAK,MAAM,CAAC,YAAY,iBAAiB,MAAM,qBAAqB;EAClE,MAAM,gBAAgB,MAAM,qBAAqB,IAAI,WAAW;AAChE,MAAI,CAAC,iBAAiB,cAAc,WAAW,EAC7C;EAIF,IAAIC,iBAAmD;EACvD,IAAI,mBAAmB;AAEvB,OAAK,MAAM,SAAS,eAAe;GACjC,MAAM,KAAK,gBAAgB,MAAM,MAAM,OAAO,UAAU;AACxD,OAAI,CAAC,kBAAkB,KAAK,kBAAkB;AAC5C,qBAAiB;AACjB,uBAAmB;;;AAKvB,OAAK,MAAM,EAAE,OAAO,MAAM,eAAe,cAAc;GACrD,MAAM,UAAU,gBAAgB,MAAM,OAAO,UAAU;AAEvD,OAAI,UAAU,iBACZ,KAAI,OAAO;IACT,SAAS,qBAAqB,WAAW,kBAAkB,QAAQ,wCAAwC,iBAAiB;IAC5H;IACA,UAAU,MAAM;IAChB;IACA,MAAM;KACJ;KACA,gBAAgB;KAChB,iBAAiB;KAClB;IACF,CAAC;;;GAKX;;;;AAKD,MAAaC,wBAA8B;CACzC,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAa,SAAS;EAAM;CACnD;CACD"}
@@ -0,0 +1,32 @@
1
+ //#region src/checker/rules/alter-undefined-entity.ts
2
+ const category = "schema";
3
+ const visitor = { afterCheck(ctx) {
4
+ const { index } = ctx;
5
+ const definedEntities = new Set(index.defineEntitiesByName.keys());
6
+ for (const [entityName, alters] of index.alterEntitiesByName) if (!definedEntities.has(entityName)) for (const { entry, file, sourceMap } of alters) ctx.report({
7
+ message: `Cannot alter undefined entity '${entityName}'. Define it first using 'define-entity ${entityName}'.`,
8
+ file,
9
+ location: entry.location,
10
+ sourceMap,
11
+ data: { entityName }
12
+ });
13
+ } };
14
+ /**
15
+ * Check for alter-entity entries targeting entities that were never defined
16
+ */
17
+ const alterUndefinedEntityRule = {
18
+ code: "alter-undefined-entity",
19
+ name: "Alter Undefined Entity",
20
+ description: "alter-entity targets an undefined entity",
21
+ category,
22
+ defaultSeverity: "error",
23
+ dependencies: {
24
+ scope: "workspace",
25
+ schemas: true
26
+ },
27
+ visitor
28
+ };
29
+
30
+ //#endregion
31
+ export { alterUndefinedEntityRule };
32
+ //# sourceMappingURL=alter-undefined-entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alter-undefined-entity.js","names":["category: RuleCategory","visitor: RuleVisitor","alterUndefinedEntityRule: Rule"],"sources":["../../../src/checker/rules/alter-undefined-entity.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor, VisitorContext } from \"../visitor.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n afterCheck(ctx: VisitorContext) {\n const { index } = ctx;\n\n // Get all defined entity names\n const definedEntities = new Set(index.defineEntitiesByName.keys());\n\n // Check alter-entity entries\n for (const [entityName, alters] of index.alterEntitiesByName) {\n if (!definedEntities.has(entityName)) {\n for (const { entry, file, sourceMap } of alters) {\n ctx.report({\n message: `Cannot alter undefined entity '${entityName}'. Define it first using 'define-entity ${entityName}'.`,\n file,\n location: entry.location,\n sourceMap,\n data: { entityName },\n });\n }\n }\n }\n },\n};\n\n/**\n * Check for alter-entity entries targeting entities that were never defined\n */\nexport const alterUndefinedEntityRule: Rule = {\n code: \"alter-undefined-entity\",\n name: \"Alter Undefined Entity\",\n description: \"alter-entity targets an undefined entity\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"workspace\", schemas: true },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,WAAW,KAAqB;CAC9B,MAAM,EAAE,UAAU;CAGlB,MAAM,kBAAkB,IAAI,IAAI,MAAM,qBAAqB,MAAM,CAAC;AAGlE,MAAK,MAAM,CAAC,YAAY,WAAW,MAAM,oBACvC,KAAI,CAAC,gBAAgB,IAAI,WAAW,CAClC,MAAK,MAAM,EAAE,OAAO,MAAM,eAAe,OACvC,KAAI,OAAO;EACT,SAAS,kCAAkC,WAAW,0CAA0C,WAAW;EAC3G;EACA,UAAU,MAAM;EAChB;EACA,MAAM,EAAE,YAAY;EACrB,CAAC;GAKX;;;;AAKD,MAAaC,2BAAiC;CAC5C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAa,SAAS;EAAM;CACnD;CACD"}
@@ -0,0 +1,34 @@
1
+ //#region src/checker/rules/create-requires-section.ts
2
+ const category = "instance";
3
+ const visitor = { visitInstanceEntry(entry, ctx) {
4
+ if (entry.header.directive !== "create") return;
5
+ if ((entry.content?.children.filter((c) => c.type === "markdown_header").length ?? 0) === 0) {
6
+ const title = entry.header.title?.value ?? "(no title)";
7
+ ctx.report({
8
+ message: `Create entry '${title}' must use at least one section.`,
9
+ file: ctx.file,
10
+ location: entry.location,
11
+ sourceMap: ctx.sourceMap,
12
+ data: {
13
+ title,
14
+ entity: entry.header.entity
15
+ }
16
+ });
17
+ }
18
+ } };
19
+ /**
20
+ * Check that create entries have at least one section in their content
21
+ */
22
+ const createRequiresSectionRule = {
23
+ code: "create-requires-section",
24
+ name: "Create Requires Section",
25
+ description: "Create entry must use at least one section",
26
+ category,
27
+ defaultSeverity: "error",
28
+ dependencies: { scope: "entry" },
29
+ visitor
30
+ };
31
+
32
+ //#endregion
33
+ export { createRequiresSectionRule };
34
+ //# sourceMappingURL=create-requires-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-requires-section.js","names":["category: RuleCategory","visitor: RuleVisitor","createRequiresSectionRule: Rule"],"sources":["../../../src/checker/rules/create-requires-section.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 if (entry.header.directive !== \"create\") {\n return;\n }\n\n // Count markdown headers (sections) in content\n const content = entry.content;\n const sectionCount = content?.children.filter((c) => c.type === \"markdown_header\").length ?? 0;\n\n if (sectionCount === 0) {\n const title = entry.header.title?.value ?? \"(no title)\";\n ctx.report({\n message: `Create entry '${title}' must use at least one section.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { title, entity: entry.header.entity },\n });\n }\n },\n};\n\n/**\n * Check that create entries have at least one section in their content\n */\nexport const createRequiresSectionRule: Rule = {\n code: \"create-requires-section\",\n name: \"Create Requires Section\",\n description: \"Create entry must use at least one section\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;AAC7B,KAAI,MAAM,OAAO,cAAc,SAC7B;AAOF,MAHgB,MAAM,SACQ,SAAS,QAAQ,MAAM,EAAE,SAAS,kBAAkB,CAAC,UAAU,OAExE,GAAG;EACtB,MAAM,QAAQ,MAAM,OAAO,OAAO,SAAS;AAC3C,MAAI,OAAO;GACT,SAAS,iBAAiB,MAAM;GAChC,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IAAE;IAAO,QAAQ,MAAM,OAAO;IAAQ;GAC7C,CAAC;;GAGP;;;;AAKD,MAAaC,4BAAkC;CAC7C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,31 @@
1
+ //#region src/checker/rules/define-entity-requires-section.ts
2
+ const category = "schema";
3
+ const visitor = { visitSchemaEntry(entry, ctx) {
4
+ if (entry.header.directive !== "define-entity") return;
5
+ if ((entry.sectionsBlock?.sections.length ?? 0) === 0) {
6
+ const entityName = entry.header.entityName.value;
7
+ ctx.report({
8
+ message: `Entity definition '${entityName}' must have at least one section.`,
9
+ file: ctx.file,
10
+ location: entry.location,
11
+ sourceMap: ctx.sourceMap,
12
+ data: { entityName }
13
+ });
14
+ }
15
+ } };
16
+ /**
17
+ * Check that define-entity entries have at least one section defined
18
+ */
19
+ const defineEntityRequiresSectionRule = {
20
+ code: "define-entity-requires-section",
21
+ name: "Define Entity Requires Section",
22
+ description: "Entity definition must have at least one section",
23
+ category,
24
+ defaultSeverity: "error",
25
+ dependencies: { scope: "entry" },
26
+ visitor
27
+ };
28
+
29
+ //#endregion
30
+ export { defineEntityRequiresSectionRule };
31
+ //# sourceMappingURL=define-entity-requires-section.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-entity-requires-section.js","names":["category: RuleCategory","visitor: RuleVisitor","defineEntityRequiresSectionRule: Rule"],"sources":["../../../src/checker/rules/define-entity-requires-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 !== \"define-entity\") {\n return;\n }\n\n const sectionCount = entry.sectionsBlock?.sections.length ?? 0;\n if (sectionCount === 0) {\n const entityName = entry.header.entityName.value;\n ctx.report({\n message: `Entity definition '${entityName}' must have at least one section.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { entityName },\n });\n }\n },\n};\n\n/**\n * Check that define-entity entries have at least one section defined\n */\nexport const defineEntityRequiresSectionRule: Rule = {\n code: \"define-entity-requires-section\",\n name: \"Define Entity Requires Section\",\n description: \"Entity definition must have at least one section\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;AAC3B,KAAI,MAAM,OAAO,cAAc,gBAC7B;AAIF,MADqB,MAAM,eAAe,SAAS,UAAU,OACxC,GAAG;EACtB,MAAM,aAAa,MAAM,OAAO,WAAW;AAC3C,MAAI,OAAO;GACT,SAAS,sBAAsB,WAAW;GAC1C,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM,EAAE,YAAY;GACrB,CAAC;;GAGP;;;;AAKD,MAAaC,kCAAwC;CACnD,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,37 @@
1
+ //#region src/checker/rules/duplicate-entity-definition.ts
2
+ const category = "schema";
3
+ const visitor = { afterCheck(ctx) {
4
+ const { index } = ctx;
5
+ for (const [entityName, defs] of index.defineEntitiesByName) if (defs.length > 1) for (const { entry, file, sourceMap } of defs) {
6
+ const otherLocations = defs.filter((d) => d.entry !== entry).map((d) => `${d.file}:${d.entry.location.startPosition.row + 1}`).join(", ");
7
+ ctx.report({
8
+ message: `Duplicate definition for entity '${entityName}'. Also defined at: ${otherLocations}`,
9
+ file,
10
+ location: entry.location,
11
+ sourceMap,
12
+ data: {
13
+ entityName,
14
+ otherLocations
15
+ }
16
+ });
17
+ }
18
+ } };
19
+ /**
20
+ * Check for multiple define-entity entries for the same entity name
21
+ */
22
+ const duplicateEntityDefinitionRule = {
23
+ code: "duplicate-entity-definition",
24
+ name: "Duplicate Entity Definition",
25
+ description: "Multiple define-entity for the same entity name",
26
+ category,
27
+ defaultSeverity: "error",
28
+ dependencies: {
29
+ scope: "workspace",
30
+ schemas: true
31
+ },
32
+ visitor
33
+ };
34
+
35
+ //#endregion
36
+ export { duplicateEntityDefinitionRule };
37
+ //# sourceMappingURL=duplicate-entity-definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-entity-definition.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateEntityDefinitionRule: Rule"],"sources":["../../../src/checker/rules/duplicate-entity-definition.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor, VisitorContext } from \"../visitor.js\";\n\nconst category: RuleCategory = \"schema\";\n\nconst visitor: RuleVisitor = {\n afterCheck(ctx: VisitorContext) {\n const { index } = ctx;\n\n // Report duplicates\n for (const [entityName, defs] of index.defineEntitiesByName) {\n if (defs.length > 1) {\n for (const { entry, file, sourceMap } of defs) {\n const otherLocations = defs\n .filter((d) => d.entry !== entry)\n .map((d) => `${d.file}:${d.entry.location.startPosition.row + 1}`)\n .join(\", \");\n\n ctx.report({\n message: `Duplicate definition for entity '${entityName}'. Also defined at: ${otherLocations}`,\n file,\n location: entry.location,\n sourceMap,\n data: { entityName, otherLocations },\n });\n }\n }\n }\n },\n};\n\n/**\n * Check for multiple define-entity entries for the same entity name\n */\nexport const duplicateEntityDefinitionRule: Rule = {\n code: \"duplicate-entity-definition\",\n name: \"Duplicate Entity Definition\",\n description: \"Multiple define-entity for the same entity name\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"workspace\", schemas: true },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,WAAW,KAAqB;CAC9B,MAAM,EAAE,UAAU;AAGlB,MAAK,MAAM,CAAC,YAAY,SAAS,MAAM,qBACrC,KAAI,KAAK,SAAS,EAChB,MAAK,MAAM,EAAE,OAAO,MAAM,eAAe,MAAM;EAC7C,MAAM,iBAAiB,KACpB,QAAQ,MAAM,EAAE,UAAU,MAAM,CAChC,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,SAAS,cAAc,MAAM,IAAI,CACjE,KAAK,KAAK;AAEb,MAAI,OAAO;GACT,SAAS,oCAAoC,WAAW,sBAAsB;GAC9E;GACA,UAAU,MAAM;GAChB;GACA,MAAM;IAAE;IAAY;IAAgB;GACrC,CAAC;;GAKX;;;;AAKD,MAAaC,gCAAsC;CACjD,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAa,SAAS;EAAM;CACnD;CACD"}
@@ -0,0 +1,38 @@
1
+ //#region src/checker/rules/duplicate-field-in-schema.ts
2
+ const category = "schema";
3
+ const visitor = { visitSchemaEntry(entry, ctx) {
4
+ const fields = entry.metadataBlock?.fields ?? [];
5
+ const seenFields = /* @__PURE__ */ new Map();
6
+ for (const field of fields) {
7
+ const fieldName = field.name.value;
8
+ const fieldLine = field.location?.startPosition.row ?? 0;
9
+ const existingLine = seenFields.get(fieldName);
10
+ if (existingLine !== void 0) ctx.report({
11
+ message: `Duplicate field '${fieldName}' in schema entry. First defined at line ${existingLine}.`,
12
+ file: ctx.file,
13
+ location: field.location,
14
+ sourceMap: ctx.sourceMap,
15
+ data: {
16
+ fieldName,
17
+ firstLine: existingLine
18
+ }
19
+ });
20
+ else seenFields.set(fieldName, fieldLine + 1);
21
+ }
22
+ } };
23
+ /**
24
+ * Check for duplicate field names within a single schema entry
25
+ */
26
+ const duplicateFieldInSchemaRule = {
27
+ code: "duplicate-field-in-schema",
28
+ name: "Duplicate Field in Schema",
29
+ description: "Same field defined twice in a schema entry",
30
+ category,
31
+ defaultSeverity: "error",
32
+ dependencies: { scope: "entry" },
33
+ visitor
34
+ };
35
+
36
+ //#endregion
37
+ export { duplicateFieldInSchemaRule };
38
+ //# sourceMappingURL=duplicate-field-in-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-field-in-schema.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateFieldInSchemaRule: Rule"],"sources":["../../../src/checker/rules/duplicate-field-in-schema.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 const fields = entry.metadataBlock?.fields ?? [];\n const seenFields = new Map<string, number>();\n\n for (const field of fields) {\n const fieldName = field.name.value;\n const fieldLine = field.location?.startPosition.row ?? 0;\n const existingLine = seenFields.get(fieldName);\n\n if (existingLine !== undefined) {\n ctx.report({\n message: `Duplicate field '${fieldName}' in schema entry. First defined at line ${existingLine}.`,\n file: ctx.file,\n location: field.location,\n sourceMap: ctx.sourceMap,\n data: { fieldName, firstLine: existingLine },\n });\n } else {\n seenFields.set(fieldName, fieldLine + 1);\n }\n }\n },\n};\n\n/**\n * Check for duplicate field names within a single schema entry\n */\nexport const duplicateFieldInSchemaRule: Rule = {\n code: \"duplicate-field-in-schema\",\n name: \"Duplicate Field in Schema\",\n description: \"Same field defined twice in a schema entry\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;CAC3B,MAAM,SAAS,MAAM,eAAe,UAAU,EAAE;CAChD,MAAM,6BAAa,IAAI,KAAqB;AAE5C,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,YAAY,MAAM,KAAK;EAC7B,MAAM,YAAY,MAAM,UAAU,cAAc,OAAO;EACvD,MAAM,eAAe,WAAW,IAAI,UAAU;AAE9C,MAAI,iBAAiB,OACnB,KAAI,OAAO;GACT,SAAS,oBAAoB,UAAU,2CAA2C,aAAa;GAC/F,MAAM,IAAI;GACV,UAAU,MAAM;GAChB,WAAW,IAAI;GACf,MAAM;IAAE;IAAW,WAAW;IAAc;GAC7C,CAAC;MAEF,YAAW,IAAI,WAAW,YAAY,EAAE;;GAI/C;;;;AAKD,MAAaC,6BAAmC;CAC9C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,52 @@
1
+ //#region src/checker/rules/duplicate-link-id.ts
2
+ const category = "link";
3
+ const visitor = { afterCheck(ctx) {
4
+ const { workspace } = ctx;
5
+ const definitionsByLinkId = /* @__PURE__ */ new Map();
6
+ for (const model of workspace.allModels()) for (const [linkId, def] of model.linkIndex.definitions) {
7
+ const defs = definitionsByLinkId.get(linkId) ?? [];
8
+ defs.push(def);
9
+ definitionsByLinkId.set(linkId, defs);
10
+ }
11
+ for (const [linkId, defs] of definitionsByLinkId) if (defs.length > 1) {
12
+ const explicitDefs = defs.filter((d) => {
13
+ const entry = d.entry;
14
+ if (entry.type === "instance_entry" || entry.type === "schema_entry") return entry.header.link?.id === linkId;
15
+ if (entry.type === "synthesis_entry") return entry.header.linkId.id === linkId;
16
+ return false;
17
+ });
18
+ if (explicitDefs.length > 1) for (const def of explicitDefs) {
19
+ const model = workspace.getModel(def.file);
20
+ const otherFiles = explicitDefs.filter((d) => d !== def).map((d) => d.file).join(", ");
21
+ ctx.report({
22
+ message: `Duplicate link ID '^${linkId}'. Also defined in: ${otherFiles}`,
23
+ file: def.file,
24
+ location: def.location,
25
+ sourceMap: model?.sourceMap,
26
+ data: {
27
+ linkId,
28
+ otherFiles
29
+ }
30
+ });
31
+ }
32
+ }
33
+ } };
34
+ /**
35
+ * Check for duplicate link ID definitions
36
+ */
37
+ const duplicateLinkIdRule = {
38
+ code: "duplicate-link-id",
39
+ name: "Duplicate Link ID",
40
+ description: "Same explicit ^link-id defined multiple times",
41
+ category,
42
+ defaultSeverity: "error",
43
+ dependencies: {
44
+ scope: "workspace",
45
+ links: true
46
+ },
47
+ visitor
48
+ };
49
+
50
+ //#endregion
51
+ export { duplicateLinkIdRule };
52
+ //# sourceMappingURL=duplicate-link-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-link-id.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateLinkIdRule: Rule"],"sources":["../../../src/checker/rules/duplicate-link-id.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor, VisitorContext } from \"../visitor.js\";\nimport type { LinkDefinition } from \"../../semantic/analyzer.js\";\n\nconst category: RuleCategory = \"link\";\n\nconst visitor: RuleVisitor = {\n afterCheck(ctx: VisitorContext) {\n const { workspace } = ctx;\n\n // Track all definitions we've seen for each link ID\n const definitionsByLinkId = new Map<string, LinkDefinition[]>();\n\n // Collect definitions from all models\n for (const model of workspace.allModels()) {\n for (const [linkId, def] of model.linkIndex.definitions) {\n const defs = definitionsByLinkId.get(linkId) ?? [];\n defs.push(def);\n definitionsByLinkId.set(linkId, defs);\n }\n }\n\n // Report duplicates\n for (const [linkId, defs] of definitionsByLinkId) {\n if (defs.length > 1) {\n // Skip timestamps - they're implicitly unique per entry\n // Only report explicit ^link-id duplicates\n const explicitDefs = defs.filter((d) => {\n const entry = d.entry;\n if (entry.type === \"instance_entry\" || entry.type === \"schema_entry\") {\n return entry.header.link?.id === linkId;\n }\n if (entry.type === \"synthesis_entry\") {\n return entry.header.linkId.id === linkId;\n }\n return false;\n });\n\n if (explicitDefs.length > 1) {\n for (const def of explicitDefs) {\n const model = workspace.getModel(def.file);\n const otherFiles = explicitDefs\n .filter((d) => d !== def)\n .map((d) => d.file)\n .join(\", \");\n\n ctx.report({\n message: `Duplicate link ID '^${linkId}'. Also defined in: ${otherFiles}`,\n file: def.file,\n location: def.location,\n sourceMap: model?.sourceMap,\n data: { linkId, otherFiles },\n });\n }\n }\n }\n }\n },\n};\n\n/**\n * Check for duplicate link ID definitions\n */\nexport const duplicateLinkIdRule: Rule = {\n code: \"duplicate-link-id\",\n name: \"Duplicate Link ID\",\n description: \"Same explicit ^link-id defined multiple times\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"workspace\", links: true },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,WAAW,KAAqB;CAC9B,MAAM,EAAE,cAAc;CAGtB,MAAM,sCAAsB,IAAI,KAA+B;AAG/D,MAAK,MAAM,SAAS,UAAU,WAAW,CACvC,MAAK,MAAM,CAAC,QAAQ,QAAQ,MAAM,UAAU,aAAa;EACvD,MAAM,OAAO,oBAAoB,IAAI,OAAO,IAAI,EAAE;AAClD,OAAK,KAAK,IAAI;AACd,sBAAoB,IAAI,QAAQ,KAAK;;AAKzC,MAAK,MAAM,CAAC,QAAQ,SAAS,oBAC3B,KAAI,KAAK,SAAS,GAAG;EAGnB,MAAM,eAAe,KAAK,QAAQ,MAAM;GACtC,MAAM,QAAQ,EAAE;AAChB,OAAI,MAAM,SAAS,oBAAoB,MAAM,SAAS,eACpD,QAAO,MAAM,OAAO,MAAM,OAAO;AAEnC,OAAI,MAAM,SAAS,kBACjB,QAAO,MAAM,OAAO,OAAO,OAAO;AAEpC,UAAO;IACP;AAEF,MAAI,aAAa,SAAS,EACxB,MAAK,MAAM,OAAO,cAAc;GAC9B,MAAM,QAAQ,UAAU,SAAS,IAAI,KAAK;GAC1C,MAAM,aAAa,aAChB,QAAQ,MAAM,MAAM,IAAI,CACxB,KAAK,MAAM,EAAE,KAAK,CAClB,KAAK,KAAK;AAEb,OAAI,OAAO;IACT,SAAS,uBAAuB,OAAO,sBAAsB;IAC7D,MAAM,IAAI;IACV,UAAU,IAAI;IACd,WAAW,OAAO;IAClB,MAAM;KAAE;KAAQ;KAAY;IAC7B,CAAC;;;GAMb;;;;AAKD,MAAaC,sBAA4B;CACvC,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAa,OAAO;EAAM;CACjD;CACD"}
@@ -0,0 +1,21 @@
1
+ //#region src/checker/rules/duplicate-metadata-key.ts
2
+ const category = "metadata";
3
+ /**
4
+ * Check for duplicate metadata keys within a single instance entry
5
+ *
6
+ * Note: This rule is a placeholder. The parser collapses duplicate keys into a Map,
7
+ * so detection would require AST-level checking before model extraction.
8
+ */
9
+ const duplicateMetadataKeyRule = {
10
+ code: "duplicate-metadata-key",
11
+ name: "Duplicate Metadata Key",
12
+ description: "Same metadata key appears twice in an entry",
13
+ category,
14
+ defaultSeverity: "error",
15
+ dependencies: { scope: "entry" },
16
+ visitor: {}
17
+ };
18
+
19
+ //#endregion
20
+ export { duplicateMetadataKeyRule };
21
+ //# sourceMappingURL=duplicate-metadata-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-metadata-key.js","names":["category: RuleCategory","duplicateMetadataKeyRule: Rule"],"sources":["../../../src/checker/rules/duplicate-metadata-key.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\n\nconst category: RuleCategory = \"metadata\";\n\n/**\n * Check for duplicate metadata keys within a single instance entry\n *\n * Note: This rule is a placeholder. The parser collapses duplicate keys into a Map,\n * so detection would require AST-level checking before model extraction.\n */\nexport const duplicateMetadataKeyRule: Rule = {\n code: \"duplicate-metadata-key\",\n name: \"Duplicate Metadata Key\",\n description: \"Same metadata key appears twice in an entry\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor: {},\n};\n"],"mappings":";AAEA,MAAMA,WAAyB;;;;;;;AAQ/B,MAAaC,2BAAiC;CAC5C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC,SAAS,EAAE;CACZ"}
@@ -0,0 +1,41 @@
1
+ //#region src/checker/rules/duplicate-section-heading.ts
2
+ const category = "content";
3
+ /**
4
+ * Extract markdown headers from entry content
5
+ */
6
+ function getMarkdownHeaders(entry) {
7
+ if (!entry.content) return [];
8
+ return entry.content.children.filter((c) => c.type === "markdown_header");
9
+ }
10
+ const visitor = { visitInstanceEntry(entry, ctx) {
11
+ const headers = getMarkdownHeaders(entry);
12
+ const seenSections = /* @__PURE__ */ new Map();
13
+ for (const header of headers) {
14
+ const match = header.text.match(/^#+\s*(.+)$/);
15
+ const sectionName = match ? match[1].trim() : header.text;
16
+ if (seenSections.get(sectionName)) ctx.report({
17
+ message: `Duplicate section heading '# ${sectionName}' in entry content.`,
18
+ file: ctx.file,
19
+ location: header.location,
20
+ sourceMap: ctx.sourceMap,
21
+ data: { sectionName }
22
+ });
23
+ else seenSections.set(sectionName, header);
24
+ }
25
+ } };
26
+ /**
27
+ * Check for duplicate section headings within a single entry's content
28
+ */
29
+ const duplicateSectionHeadingRule = {
30
+ code: "duplicate-section-heading",
31
+ name: "Duplicate Section Heading",
32
+ description: "Same # Section appears twice in entry content",
33
+ category,
34
+ defaultSeverity: "error",
35
+ dependencies: { scope: "entry" },
36
+ visitor
37
+ };
38
+
39
+ //#endregion
40
+ export { duplicateSectionHeadingRule };
41
+ //# sourceMappingURL=duplicate-section-heading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-section-heading.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateSectionHeadingRule: Rule"],"sources":["../../../src/checker/rules/duplicate-section-heading.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport type { InstanceEntry, MarkdownHeader } from \"../../ast/ast-types.js\";\n\nconst category: RuleCategory = \"content\";\n\n/**\n * Extract markdown headers from entry content\n */\nfunction getMarkdownHeaders(entry: InstanceEntry): MarkdownHeader[] {\n if (!entry.content) {\n return [];\n }\n return entry.content.children.filter((c): c is MarkdownHeader => c.type === \"markdown_header\");\n}\n\nconst visitor: RuleVisitor = {\n visitInstanceEntry(entry, ctx) {\n const headers = getMarkdownHeaders(entry);\n const seenSections = new Map<string, MarkdownHeader>();\n\n for (const header of headers) {\n // Extract section name from \"# SectionName\" format\n const match = header.text.match(/^#+\\s*(.+)$/);\n const sectionName = match ? match[1].trim() : header.text;\n const existing = seenSections.get(sectionName);\n\n if (existing) {\n ctx.report({\n message: `Duplicate section heading '# ${sectionName}' in entry content.`,\n file: ctx.file,\n location: header.location,\n sourceMap: ctx.sourceMap,\n data: { sectionName },\n });\n } else {\n seenSections.set(sectionName, header);\n }\n }\n },\n};\n\n/**\n * Check for duplicate section headings within a single entry's content\n */\nexport const duplicateSectionHeadingRule: Rule = {\n code: \"duplicate-section-heading\",\n name: \"Duplicate Section Heading\",\n description: \"Same # Section appears twice in entry content\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;;;;AAK/B,SAAS,mBAAmB,OAAwC;AAClE,KAAI,CAAC,MAAM,QACT,QAAO,EAAE;AAEX,QAAO,MAAM,QAAQ,SAAS,QAAQ,MAA2B,EAAE,SAAS,kBAAkB;;AAGhG,MAAMC,UAAuB,EAC3B,mBAAmB,OAAO,KAAK;CAC7B,MAAM,UAAU,mBAAmB,MAAM;CACzC,MAAM,+BAAe,IAAI,KAA6B;AAEtD,MAAK,MAAM,UAAU,SAAS;EAE5B,MAAM,QAAQ,OAAO,KAAK,MAAM,cAAc;EAC9C,MAAM,cAAc,QAAQ,MAAM,GAAG,MAAM,GAAG,OAAO;AAGrD,MAFiB,aAAa,IAAI,YAAY,CAG5C,KAAI,OAAO;GACT,SAAS,gCAAgC,YAAY;GACrD,MAAM,IAAI;GACV,UAAU,OAAO;GACjB,WAAW,IAAI;GACf,MAAM,EAAE,aAAa;GACtB,CAAC;MAEF,cAAa,IAAI,aAAa,OAAO;;GAI5C;;;;AAKD,MAAaC,8BAAoC;CAC/C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,38 @@
1
+ //#region src/checker/rules/duplicate-section-in-schema.ts
2
+ const category = "schema";
3
+ const visitor = { visitSchemaEntry(entry, ctx) {
4
+ const sections = entry.sectionsBlock?.sections ?? [];
5
+ const seenSections = /* @__PURE__ */ new Map();
6
+ for (const section of sections) {
7
+ const sectionName = section.name.value;
8
+ const sectionLine = section.location?.startPosition.row ?? 0;
9
+ const existingLine = seenSections.get(sectionName);
10
+ if (existingLine !== void 0) ctx.report({
11
+ message: `Duplicate section '${sectionName}' in schema entry. First defined at line ${existingLine}.`,
12
+ file: ctx.file,
13
+ location: section.location,
14
+ sourceMap: ctx.sourceMap,
15
+ data: {
16
+ sectionName,
17
+ firstLine: existingLine
18
+ }
19
+ });
20
+ else seenSections.set(sectionName, sectionLine + 1);
21
+ }
22
+ } };
23
+ /**
24
+ * Check for duplicate section names within a single schema entry
25
+ */
26
+ const duplicateSectionInSchemaRule = {
27
+ code: "duplicate-section-in-schema",
28
+ name: "Duplicate Section in Schema",
29
+ description: "Same section defined twice in a schema entry",
30
+ category,
31
+ defaultSeverity: "error",
32
+ dependencies: { scope: "entry" },
33
+ visitor
34
+ };
35
+
36
+ //#endregion
37
+ export { duplicateSectionInSchemaRule };
38
+ //# sourceMappingURL=duplicate-section-in-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-section-in-schema.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateSectionInSchemaRule: Rule"],"sources":["../../../src/checker/rules/duplicate-section-in-schema.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 const sections = entry.sectionsBlock?.sections ?? [];\n const seenSections = new Map<string, number>();\n\n for (const section of sections) {\n const sectionName = section.name.value;\n const sectionLine = section.location?.startPosition.row ?? 0;\n const existingLine = seenSections.get(sectionName);\n\n if (existingLine !== undefined) {\n ctx.report({\n message: `Duplicate section '${sectionName}' in schema entry. First defined at line ${existingLine}.`,\n file: ctx.file,\n location: section.location,\n sourceMap: ctx.sourceMap,\n data: { sectionName, firstLine: existingLine },\n });\n } else {\n seenSections.set(sectionName, sectionLine + 1);\n }\n }\n },\n};\n\n/**\n * Check for duplicate section names within a single schema entry\n */\nexport const duplicateSectionInSchemaRule: Rule = {\n code: \"duplicate-section-in-schema\",\n name: \"Duplicate Section in Schema\",\n description: \"Same section defined twice in a schema entry\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,iBAAiB,OAAO,KAAK;CAC3B,MAAM,WAAW,MAAM,eAAe,YAAY,EAAE;CACpD,MAAM,+BAAe,IAAI,KAAqB;AAE9C,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,cAAc,QAAQ,KAAK;EACjC,MAAM,cAAc,QAAQ,UAAU,cAAc,OAAO;EAC3D,MAAM,eAAe,aAAa,IAAI,YAAY;AAElD,MAAI,iBAAiB,OACnB,KAAI,OAAO;GACT,SAAS,sBAAsB,YAAY,2CAA2C,aAAa;GACnG,MAAM,IAAI;GACV,UAAU,QAAQ;GAClB,WAAW,IAAI;GACf,MAAM;IAAE;IAAa,WAAW;IAAc;GAC/C,CAAC;MAEF,cAAa,IAAI,aAAa,cAAc,EAAE;;GAIrD;;;;AAKD,MAAaC,+BAAqC;CAChD,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,104 @@
1
+ //#region src/checker/rules/duplicate-timestamp.ts
2
+ const category = "instance";
3
+ const visitor = { afterCheck(ctx) {
4
+ const { workspace } = ctx;
5
+ for (const model of workspace.allModels()) {
6
+ const entriesByIdentity = /* @__PURE__ */ new Map();
7
+ for (const entry of model.ast.entries) {
8
+ const timestamp = getEntryTimestamp(entry);
9
+ if (!timestamp) continue;
10
+ if (hasExplicitLinkId(entry)) continue;
11
+ const identityKey = `${timestamp}|${entry.type}`;
12
+ const entries = entriesByIdentity.get(identityKey) ?? [];
13
+ entries.push(entry);
14
+ entriesByIdentity.set(identityKey, entries);
15
+ }
16
+ for (const [identityKey, entries] of entriesByIdentity) if (entries.length > 1) {
17
+ const [timestamp, entryType] = identityKey.split("|");
18
+ const entryTypeName = formatEntryType(entryType);
19
+ for (const entry of entries) ctx.report({
20
+ message: `Duplicate timestamp '${timestamp}' for ${entryTypeName} without explicit ^link-id. Add a unique ^link-id to disambiguate entries with the same timestamp.`,
21
+ file: model.file,
22
+ location: getTimestampLocation(entry),
23
+ sourceMap: model.sourceMap,
24
+ data: {
25
+ timestamp,
26
+ entryType,
27
+ duplicateCount: entries.length
28
+ }
29
+ });
30
+ }
31
+ }
32
+ } };
33
+ /**
34
+ * Get timestamp string from an entry
35
+ */
36
+ function getEntryTimestamp(entry) {
37
+ switch (entry.type) {
38
+ case "instance_entry":
39
+ case "schema_entry":
40
+ case "synthesis_entry":
41
+ case "actualize_entry": return entry.header.timestamp.value;
42
+ default: return null;
43
+ }
44
+ }
45
+ /**
46
+ * Check if entry has an explicit link ID
47
+ */
48
+ function hasExplicitLinkId(entry) {
49
+ switch (entry.type) {
50
+ case "instance_entry":
51
+ case "schema_entry": return entry.header.link !== null;
52
+ case "synthesis_entry": return true;
53
+ case "actualize_entry": return true;
54
+ default: return false;
55
+ }
56
+ }
57
+ /**
58
+ * Get the location of the timestamp in an entry (for error reporting)
59
+ */
60
+ function getTimestampLocation(entry) {
61
+ switch (entry.type) {
62
+ case "instance_entry":
63
+ case "schema_entry":
64
+ case "synthesis_entry":
65
+ case "actualize_entry": return entry.header.timestamp.location;
66
+ default: return entry.location;
67
+ }
68
+ }
69
+ /**
70
+ * Format entry type for human-readable error messages
71
+ */
72
+ function formatEntryType(entryType) {
73
+ switch (entryType) {
74
+ case "instance_entry": return "instance entry";
75
+ case "schema_entry": return "schema entry";
76
+ case "synthesis_entry": return "synthesis entry";
77
+ case "actualize_entry": return "actualize entry";
78
+ default: return entryType;
79
+ }
80
+ }
81
+ /**
82
+ * Check for duplicate timestamps without link IDs
83
+ *
84
+ * When multiple entries in the same file have identical timestamps and the same
85
+ * entry type, they must have explicit ^link-id values to be distinguished during
86
+ * merge operations. Without link IDs, only the first entry will be used during
87
+ * three-way merges, potentially causing data loss.
88
+ */
89
+ const duplicateTimestampRule = {
90
+ code: "duplicate-timestamp",
91
+ name: "Duplicate Timestamp Without Link ID",
92
+ description: "Multiple entries with the same timestamp and type must have explicit ^link-id for merge disambiguation",
93
+ category,
94
+ defaultSeverity: "error",
95
+ dependencies: {
96
+ scope: "document",
97
+ links: false
98
+ },
99
+ visitor
100
+ };
101
+
102
+ //#endregion
103
+ export { duplicateTimestampRule };
104
+ //# sourceMappingURL=duplicate-timestamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-timestamp.js","names":["category: RuleCategory","visitor: RuleVisitor","duplicateTimestampRule: Rule"],"sources":["../../../src/checker/rules/duplicate-timestamp.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor, VisitorContext } from \"../visitor.js\";\nimport type { Entry } from \"../../ast/ast-types.js\";\n\nconst category: RuleCategory = \"instance\";\n\nconst visitor: RuleVisitor = {\n afterCheck(ctx: VisitorContext) {\n const { workspace } = ctx;\n\n // Check each file independently since timestamps are per-file\n for (const model of workspace.allModels()) {\n // Track entries by timestamp+type (the merge identity key)\n const entriesByIdentity = new Map<string, Entry[]>();\n\n for (const entry of model.ast.entries) {\n // Get timestamp\n const timestamp = getEntryTimestamp(entry);\n if (!timestamp) {\n continue;\n }\n\n // Check if entry has an explicit link ID\n const hasLinkId = hasExplicitLinkId(entry);\n if (hasLinkId) {\n // Entries with link IDs are always uniquely identified\n continue;\n }\n\n // Build identity key: timestamp|type (using | since timestamps contain :)\n const identityKey = `${timestamp}|${entry.type}`;\n\n const entries = entriesByIdentity.get(identityKey) ?? [];\n entries.push(entry);\n entriesByIdentity.set(identityKey, entries);\n }\n\n // Report duplicates\n for (const [identityKey, entries] of entriesByIdentity) {\n if (entries.length > 1) {\n const [timestamp, entryType] = identityKey.split(\"|\");\n const entryTypeName = formatEntryType(entryType);\n\n for (const entry of entries) {\n ctx.report({\n message: `Duplicate timestamp '${timestamp}' for ${entryTypeName} without explicit ^link-id. Add a unique ^link-id to disambiguate entries with the same timestamp.`,\n file: model.file,\n location: getTimestampLocation(entry),\n sourceMap: model.sourceMap,\n data: { timestamp, entryType, duplicateCount: entries.length },\n });\n }\n }\n }\n }\n },\n};\n\n/**\n * Get timestamp string from an entry\n */\nfunction getEntryTimestamp(entry: Entry): string | null {\n switch (entry.type) {\n case \"instance_entry\":\n case \"schema_entry\":\n case \"synthesis_entry\":\n case \"actualize_entry\":\n return entry.header.timestamp.value;\n default:\n return null;\n }\n}\n\n/**\n * Check if entry has an explicit link ID\n */\nfunction hasExplicitLinkId(entry: Entry): boolean {\n switch (entry.type) {\n case \"instance_entry\":\n case \"schema_entry\":\n return entry.header.link !== null;\n case \"synthesis_entry\":\n // Synthesis entries always have a required linkId\n return true;\n case \"actualize_entry\":\n // Actualize entries use target, which is always present\n return true;\n default:\n return false;\n }\n}\n\n/**\n * Get the location of the timestamp in an entry (for error reporting)\n */\nfunction getTimestampLocation(entry: Entry): import(\"../../ast/ast-types.js\").Location {\n switch (entry.type) {\n case \"instance_entry\":\n case \"schema_entry\":\n case \"synthesis_entry\":\n case \"actualize_entry\":\n return entry.header.timestamp.location;\n default: {\n // This should never happen since all entry types have timestamps\n const _exhaustive: never = entry;\n return (_exhaustive as Entry).location;\n }\n }\n}\n\n/**\n * Format entry type for human-readable error messages\n */\nfunction formatEntryType(entryType: string): string {\n switch (entryType) {\n case \"instance_entry\":\n return \"instance entry\";\n case \"schema_entry\":\n return \"schema entry\";\n case \"synthesis_entry\":\n return \"synthesis entry\";\n case \"actualize_entry\":\n return \"actualize entry\";\n default:\n return entryType;\n }\n}\n\n/**\n * Check for duplicate timestamps without link IDs\n *\n * When multiple entries in the same file have identical timestamps and the same\n * entry type, they must have explicit ^link-id values to be distinguished during\n * merge operations. Without link IDs, only the first entry will be used during\n * three-way merges, potentially causing data loss.\n */\nexport const duplicateTimestampRule: Rule = {\n code: \"duplicate-timestamp\",\n name: \"Duplicate Timestamp Without Link ID\",\n description:\n \"Multiple entries with the same timestamp and type must have explicit ^link-id for merge disambiguation\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"document\", links: false },\n visitor,\n};\n"],"mappings":";AAIA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,WAAW,KAAqB;CAC9B,MAAM,EAAE,cAAc;AAGtB,MAAK,MAAM,SAAS,UAAU,WAAW,EAAE;EAEzC,MAAM,oCAAoB,IAAI,KAAsB;AAEpD,OAAK,MAAM,SAAS,MAAM,IAAI,SAAS;GAErC,MAAM,YAAY,kBAAkB,MAAM;AAC1C,OAAI,CAAC,UACH;AAKF,OADkB,kBAAkB,MAAM,CAGxC;GAIF,MAAM,cAAc,GAAG,UAAU,GAAG,MAAM;GAE1C,MAAM,UAAU,kBAAkB,IAAI,YAAY,IAAI,EAAE;AACxD,WAAQ,KAAK,MAAM;AACnB,qBAAkB,IAAI,aAAa,QAAQ;;AAI7C,OAAK,MAAM,CAAC,aAAa,YAAY,kBACnC,KAAI,QAAQ,SAAS,GAAG;GACtB,MAAM,CAAC,WAAW,aAAa,YAAY,MAAM,IAAI;GACrD,MAAM,gBAAgB,gBAAgB,UAAU;AAEhD,QAAK,MAAM,SAAS,QAClB,KAAI,OAAO;IACT,SAAS,wBAAwB,UAAU,QAAQ,cAAc;IACjE,MAAM,MAAM;IACZ,UAAU,qBAAqB,MAAM;IACrC,WAAW,MAAM;IACjB,MAAM;KAAE;KAAW;KAAW,gBAAgB,QAAQ;KAAQ;IAC/D,CAAC;;;GAMb;;;;AAKD,SAAS,kBAAkB,OAA6B;AACtD,SAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,kBACH,QAAO,MAAM,OAAO,UAAU;EAChC,QACE,QAAO;;;;;;AAOb,SAAS,kBAAkB,OAAuB;AAChD,SAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK,eACH,QAAO,MAAM,OAAO,SAAS;EAC/B,KAAK,kBAEH,QAAO;EACT,KAAK,kBAEH,QAAO;EACT,QACE,QAAO;;;;;;AAOb,SAAS,qBAAqB,OAAyD;AACrF,SAAQ,MAAM,MAAd;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,kBACH,QAAO,MAAM,OAAO,UAAU;EAChC,QAGE,QAD2B,MACG;;;;;;AAQpC,SAAS,gBAAgB,WAA2B;AAClD,SAAQ,WAAR;EACE,KAAK,iBACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,KAAK,kBACH,QAAO;EACT,KAAK,kBACH,QAAO;EACT,QACE,QAAO;;;;;;;;;;;AAYb,MAAaC,yBAA+B;CAC1C,MAAM;CACN,MAAM;CACN,aACE;CACF;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAY,OAAO;EAAO;CACjD;CACD"}
@@ -0,0 +1,45 @@
1
+ //#region src/checker/rules/empty-required-value.ts
2
+ const category = "metadata";
3
+ const visitor = { visitInstanceEntry(entry, ctx) {
4
+ const registry = ctx.workspace.schemaRegistry;
5
+ const entity = entry.header.entity;
6
+ const schema = registry.get(entity);
7
+ if (!schema) return;
8
+ for (const meta of entry.metadata) {
9
+ const fieldName = meta.key.value;
10
+ const fieldSchema = schema.fields.get(fieldName);
11
+ if (!fieldSchema) continue;
12
+ if (!fieldSchema.optional && fieldSchema.defaultValue === null) {
13
+ const rawValue = meta.value.raw.trim();
14
+ if (rawValue === "" || rawValue === "\"\"") ctx.report({
15
+ message: `Required field '${fieldName}' has an empty value.`,
16
+ file: ctx.file,
17
+ location: meta.value.location,
18
+ sourceMap: ctx.sourceMap,
19
+ data: {
20
+ fieldName,
21
+ entity
22
+ }
23
+ });
24
+ }
25
+ }
26
+ } };
27
+ /**
28
+ * Check for required fields that are present but have empty values
29
+ */
30
+ const emptyRequiredValueRule = {
31
+ code: "empty-required-value",
32
+ name: "Empty Required Value",
33
+ description: "Required field has empty value",
34
+ category,
35
+ defaultSeverity: "error",
36
+ dependencies: {
37
+ scope: "entry",
38
+ schemas: true
39
+ },
40
+ visitor
41
+ };
42
+
43
+ //#endregion
44
+ export { emptyRequiredValueRule };
45
+ //# sourceMappingURL=empty-required-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-required-value.js","names":["category: RuleCategory","visitor: RuleVisitor","emptyRequiredValueRule: Rule"],"sources":["../../../src/checker/rules/empty-required-value.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\n\nconst category: RuleCategory = \"metadata\";\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 the field is required and has an empty value\n if (!fieldSchema.optional && fieldSchema.defaultValue === null) {\n const rawValue = meta.value.raw.trim();\n // Check for empty or effectively empty values\n if (rawValue === \"\" || rawValue === '\"\"') {\n ctx.report({\n message: `Required field '${fieldName}' has an empty value.`,\n file: ctx.file,\n location: meta.value.location,\n sourceMap: ctx.sourceMap,\n data: { fieldName, entity },\n });\n }\n }\n }\n },\n};\n\n/**\n * Check for required fields that are present but have empty values\n */\nexport const emptyRequiredValueRule: Rule = {\n code: \"empty-required-value\",\n name: \"Empty Required Value\",\n description: \"Required field has empty value\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", schemas: true },\n visitor,\n};\n"],"mappings":";AAGA,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,YAAY,YAAY,YAAY,iBAAiB,MAAM;GAC9D,MAAM,WAAW,KAAK,MAAM,IAAI,MAAM;AAEtC,OAAI,aAAa,MAAM,aAAa,OAClC,KAAI,OAAO;IACT,SAAS,mBAAmB,UAAU;IACtC,MAAM,IAAI;IACV,UAAU,KAAK,MAAM;IACrB,WAAW,IAAI;IACf,MAAM;KAAE;KAAW;KAAQ;IAC5B,CAAC;;;GAKX;;;;AAKD,MAAaC,yBAA+B;CAC1C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,SAAS;EAAM;CAC/C;CACD"}