@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 @@
1
+ {"version":3,"file":"node-at-position.js","names":["current: SyntaxNode | null"],"sources":["../../src/ast/node-at-position.ts"],"sourcesContent":["import type { SyntaxNode, Point } from \"./ast-types.js\";\nimport type { ParsedDocument, ParsedBlock, GenericTree } from \"../parser.shared.js\";\nimport {\n findBlockAtPosition,\n positionToPoint,\n type SourceMap,\n type Position,\n} from \"../source-map.js\";\nimport type {\n Link,\n Tag,\n Timestamp,\n Key,\n TypeExpression,\n Identifier,\n FieldName,\n SectionName,\n Location,\n} from \"./ast-types.js\";\nimport {\n extractLink,\n extractTag,\n extractTimestamp,\n extractKey,\n extractTypeExpression,\n extractIdentifier,\n extractFieldName,\n extractSectionName,\n} from \"./extract.js\";\n\n// ===================\n// Types\n// ===================\n\n/**\n * Context for a link node (^link-id)\n */\nexport interface LinkContext {\n kind: \"link\";\n linkId: string;\n node: Link;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a tag node (#tag)\n */\nexport interface TagContext {\n kind: \"tag\";\n tagName: string;\n node: Tag;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a timestamp node\n */\nexport interface TimestampContext {\n kind: \"timestamp\";\n value: string;\n node: Timestamp;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a directive (create, update, define-entity, etc.)\n */\nexport interface DirectiveContext {\n kind: \"directive\";\n directive: string;\n location: Location;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for an entity name in instance entries\n */\nexport interface EntityContext {\n kind: \"entity\";\n entityName: string;\n location: Location;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for an entity name in schema entries (define-entity/alter-entity)\n */\nexport interface SchemaEntityContext {\n kind: \"schema_entity\";\n entityName: string;\n node: Identifier;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a metadata key\n */\nexport interface MetadataKeyContext {\n kind: \"metadata_key\";\n key: string;\n node: Key;\n /** The entity type of the containing entry (if known) */\n entityContext?: string;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a section header in content (# SectionName)\n */\nexport interface SectionHeaderContext {\n kind: \"section_header\";\n sectionName: string;\n location: Location;\n /** The entity type of the containing entry (if known) */\n entityContext?: string;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a type expression in schema definitions\n */\nexport interface TypeContext {\n kind: \"type\";\n typeName: string;\n node: TypeExpression;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a field name in schema definitions\n */\nexport interface FieldNameContext {\n kind: \"field_name\";\n fieldName: string;\n node: FieldName;\n /** The entity being defined/altered */\n entityContext?: string;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a section name in schema definitions\n */\nexport interface SectionNameContext {\n kind: \"section_name\";\n sectionName: string;\n node: SectionName;\n /** The entity being defined/altered */\n entityContext?: string;\n sourceMap: SourceMap;\n}\n\n/**\n * Context for a title\n */\nexport interface TitleContext {\n kind: \"title\";\n title: string;\n location: Location;\n sourceMap: SourceMap;\n}\n\n/**\n * Unknown context - position doesn't map to a recognized element\n */\nexport interface UnknownContext {\n kind: \"unknown\";\n}\n\n/**\n * All possible node contexts\n */\nexport type NodeContext =\n | LinkContext\n | TagContext\n | TimestampContext\n | DirectiveContext\n | EntityContext\n | SchemaEntityContext\n | MetadataKeyContext\n | SectionHeaderContext\n | TypeContext\n | FieldNameContext\n | SectionNameContext\n | TitleContext\n | UnknownContext;\n\n// ===================\n// Helpers\n// ===================\n\n/**\n * Extract location from a syntax node\n */\nfunction extractLocation(node: SyntaxNode): Location {\n return {\n startIndex: node.startIndex,\n endIndex: node.endIndex,\n startPosition: node.startPosition,\n endPosition: node.endPosition,\n };\n}\n\n/**\n * Find the entry that contains a given node by walking up the tree\n */\nfunction findContainingEntry(node: SyntaxNode): SyntaxNode | null {\n let current: SyntaxNode | null = node;\n while (current) {\n if (current.type === \"entry\") {\n return current;\n }\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Get the entity context (entity type name) from an entry node\n */\nfunction getEntityContextFromEntry(entryNode: SyntaxNode): string | undefined {\n // entry contains one of: data_entry, schema_entry\n const child = entryNode.namedChildren[0];\n if (!child) {\n return undefined;\n }\n\n if (child.type === \"data_entry\") {\n // In the new grammar, header fields are directly on data_entry\n // The directive tells us what kind of entry it is\n const directiveNode = child.childForFieldName(\"directive\");\n const directive = directiveNode?.text;\n\n if (directive === \"define-synthesis\" || directive === \"actualize-synthesis\") {\n return \"synthesis\";\n }\n // For create/update, the argument field is the entity (identifier)\n const argumentNode = child.childForFieldName(\"argument\");\n if (argumentNode?.type === \"identifier\") {\n return argumentNode.text;\n }\n } else if (child.type === \"schema_entry\") {\n // In the new grammar, header fields are directly on schema_entry\n const argumentNode = child.childForFieldName(\"argument\");\n if (argumentNode) {\n return argumentNode.text;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check if position is within a node\n */\nfunction isPositionInNode(point: Point, node: SyntaxNode): boolean {\n const start = node.startPosition;\n const end = node.endPosition;\n\n // Before start\n if (point.row < start.row || (point.row === start.row && point.column < start.column)) {\n return false;\n }\n // After end\n if (point.row > end.row || (point.row === end.row && point.column > end.column)) {\n return false;\n }\n return true;\n}\n\n// ===================\n// Directive detection\n// ===================\n\n/**\n * Check if position is on a directive keyword within an entry\n */\nfunction checkForDirective(\n node: SyntaxNode,\n point: Point,\n sourceMap: SourceMap,\n): DirectiveContext | null {\n // Walk up to find entry node (data_entry or schema_entry)\n let current: SyntaxNode | null = node;\n while (current) {\n if (current.type === \"data_entry\" || current.type === \"schema_entry\") {\n // In the new grammar, directive is directly on the entry node\n const directiveNode = current.childForFieldName(\"directive\");\n if (directiveNode && isPositionInNode(point, directiveNode)) {\n return {\n kind: \"directive\",\n directive: directiveNode.text,\n location: extractLocation(directiveNode),\n sourceMap,\n };\n }\n break;\n }\n current = current.parent;\n }\n\n return null;\n}\n\n// ===================\n// Main function\n// ===================\n\n/**\n * Find the semantic context of a node at a given position in a parsed document.\n *\n * This function handles both standalone .thalo files and embedded thalo blocks\n * in markdown files. It uses the source map to convert file-absolute positions\n * to block-relative positions before querying the AST.\n *\n * @param parsed - The parsed document (may contain multiple blocks for markdown)\n * @param position - File-absolute position (0-based line and column)\n * @returns The semantic context at the position, or { kind: \"unknown\" } if not recognized\n */\nexport function findNodeAtPosition(\n parsed: ParsedDocument<GenericTree>,\n position: Position,\n): NodeContext {\n // Find which block contains the position\n const match = findBlockAtPosition(parsed.blocks, position);\n if (!match) {\n return { kind: \"unknown\" };\n }\n\n const { block, blockPosition } = match;\n const point = positionToPoint(blockPosition);\n\n // Use tree-sitter to find the deepest node at this position\n // Type assertion: both native and web tree-sitter rootNode have compatible interfaces\n const rootNode = block.tree.rootNode as SyntaxNode;\n let node = rootNode.descendantForPosition(point);\n\n if (!node) {\n return { kind: \"unknown\" };\n }\n\n // Handle edge case: position is at the exact end of a node\n // In this case, descendantForPosition might return the parent node\n // Try to find a child node that ends exactly at this position\n const endMatchingChild = findChildEndingAt(node, point);\n if (endMatchingChild) {\n node = endMatchingChild;\n }\n\n return classifyNode(node, point, block);\n}\n\n/**\n * Find a child node that ends exactly at the given position.\n * This helps handle edge cases where cursor is right at the end of a token.\n */\nfunction findChildEndingAt(node: SyntaxNode, point: Point): SyntaxNode | null {\n for (const child of node.namedChildren) {\n if (!child) {\n continue;\n } // Skip nulls\n const end = child.endPosition;\n // Check if this child ends exactly at the point\n if (end.row === point.row && end.column === point.column) {\n // Recursively check for deeper matches\n const deeper = findChildEndingAt(child, point);\n return deeper || child;\n }\n // Check if point is within this child\n if (isPositionInNode(point, child)) {\n return findChildEndingAt(child, point);\n }\n }\n return null;\n}\n\n/**\n * Check if a node is inside an ERROR context (parse error recovery)\n */\nfunction isInsideErrorNode(node: SyntaxNode): boolean {\n let current: SyntaxNode | null = node;\n while (current) {\n if (current.type === \"ERROR\") {\n return true;\n }\n current = current.parent;\n }\n return false;\n}\n\n/**\n * Classify a syntax node into a semantic context\n */\nfunction classifyNode(node: SyntaxNode, point: Point, block: ParsedBlock): NodeContext {\n const { sourceMap } = block;\n\n // Direct node type matches\n switch (node.type) {\n case \"link\":\n return {\n kind: \"link\",\n linkId: extractLink(node).id,\n node: extractLink(node),\n sourceMap,\n };\n\n case \"tag\":\n return {\n kind: \"tag\",\n tagName: extractTag(node).name,\n node: extractTag(node),\n sourceMap,\n };\n\n case \"timestamp\":\n return {\n kind: \"timestamp\",\n value: extractTimestamp(node).value,\n node: extractTimestamp(node),\n sourceMap,\n };\n\n // Timestamp child nodes - delegate to parent timestamp\n case \"timestamp_date\":\n case \"timestamp_t\":\n case \"timestamp_time\":\n case \"timestamp_tz\": {\n const parent = node.parent;\n if (parent && parent.type === \"timestamp\") {\n return {\n kind: \"timestamp\",\n value: extractTimestamp(parent).value,\n node: extractTimestamp(parent),\n sourceMap,\n };\n }\n return { kind: \"unknown\" };\n }\n\n case \"key\":\n return classifyKeyNode(node, sourceMap);\n\n case \"primitive_type\":\n case \"literal_type\":\n case \"array_type\":\n case \"union_type\": {\n const typeExpr = extractTypeExpression(node.parent ?? node);\n // Skip syntax errors (unknown types)\n if (typeExpr.type === \"syntax_error\") {\n return { kind: \"unknown\" };\n }\n return {\n kind: \"type\",\n typeName: node.type === \"primitive_type\" ? node.text : node.type,\n node: typeExpr,\n sourceMap,\n };\n }\n\n case \"field_name\":\n return classifyFieldNameNode(node, sourceMap);\n\n case \"section_name\":\n return classifySectionNameNode(node, sourceMap);\n\n case \"title\": {\n // Don't provide hover for titles inside ERROR contexts - they may be misidentified\n // during error recovery (e.g., \"fact\" in `type: \"fact\"` without entry header)\n if (isInsideErrorNode(node)) {\n return { kind: \"unknown\" };\n }\n // Handle both closed (\"text\") and unclosed (\"text) titles\n const text = node.text;\n const hasClosingQuote = text.endsWith('\"') && text.length > 1;\n const title = hasClosingQuote ? text.slice(1, -1) : text.slice(1);\n return {\n kind: \"title\",\n title,\n location: extractLocation(node),\n sourceMap,\n };\n }\n\n case \"identifier\":\n // Could be entity name in schema header\n return classifyIdentifierNode(node, sourceMap);\n\n case \"markdown_header\":\n // Section header in content\n return classifyMarkdownHeader(node, sourceMap);\n }\n\n // Check if we're on a directive keyword\n const directiveContext = checkForDirective(node, point, sourceMap);\n if (directiveContext) {\n return directiveContext;\n }\n\n // Check parent nodes for context\n let current: SyntaxNode | null = node;\n while (current) {\n // Check for entity in data_entry (argument field for create/update)\n if (current.type === \"data_entry\") {\n const directiveNode = current.childForFieldName(\"directive\");\n const directive = directiveNode?.text;\n\n // For create/update directives, the argument is the entity name\n if (directive === \"create\" || directive === \"update\") {\n const argumentNode = current.childForFieldName(\"argument\");\n if (\n argumentNode &&\n argumentNode.type === \"identifier\" &&\n isPositionInNode(point, argumentNode)\n ) {\n return {\n kind: \"entity\",\n entityName: argumentNode.text,\n location: extractLocation(argumentNode),\n sourceMap,\n };\n }\n }\n }\n\n // Check for entity_name in schema_entry (argument field for define-entity/alter-entity)\n if (current.type === \"schema_entry\") {\n const argumentNode = current.childForFieldName(\"argument\");\n if (argumentNode && isPositionInNode(point, argumentNode)) {\n return {\n kind: \"schema_entity\",\n entityName: argumentNode.text,\n node: extractIdentifier(argumentNode),\n sourceMap,\n };\n }\n }\n\n current = current.parent;\n }\n\n // Fallback: check if we're on text that looks like an entity position\n // This handles cases where the parser couldn't match the entity (unknown entity names)\n const entityFallback = checkForUnrecognizedEntity(node, point, sourceMap, block);\n if (entityFallback) {\n return entityFallback;\n }\n\n // Fallback: check if we're on a section header in content\n const sectionFallback = checkForSectionHeader(node, point, sourceMap, block);\n if (sectionFallback) {\n return sectionFallback;\n }\n\n return { kind: \"unknown\" };\n}\n\n/**\n * Check if we're on text that looks like an entity name but wasn't recognized by parser.\n * This handles the case of unknown/invalid entity names.\n */\nfunction checkForUnrecognizedEntity(\n _node: SyntaxNode,\n point: Point,\n sourceMap: SourceMap,\n block: ParsedBlock,\n): EntityContext | null {\n // Look for ERROR nodes or unrecognized text after \"create\" or \"update\"\n const lineText = getLineTextFromBlock(block, point.row);\n if (!lineText) {\n return null;\n }\n\n // Check if line starts with timestamp + create/update pattern\n const match = lineText.match(\n /^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?:Z|[+-]\\d{2}:\\d{2}))\\s+(create|update)\\s+([a-z][a-zA-Z0-9\\-_]*)/,\n );\n if (!match) {\n return null;\n }\n\n const [fullMatch, , , entityName] = match;\n const entityStart = fullMatch.lastIndexOf(entityName);\n const entityEnd = entityStart + entityName.length;\n\n // Check if position is within the entity name\n if (point.column >= entityStart && point.column <= entityEnd) {\n return {\n kind: \"entity\",\n entityName,\n location: {\n startIndex: 0,\n endIndex: 0,\n startPosition: { row: point.row, column: entityStart },\n endPosition: { row: point.row, column: entityEnd },\n },\n sourceMap,\n };\n }\n\n return null;\n}\n\n/**\n * Get line text from a parsed block\n */\nfunction getLineTextFromBlock(block: ParsedBlock, lineNumber: number): string | null {\n const lines = block.source.split(\"\\n\");\n if (lineNumber >= 0 && lineNumber < lines.length) {\n return lines[lineNumber];\n }\n return null;\n}\n\n/**\n * Check if we're on a section header in content (# SectionName)\n * This handles cases where the parser might not properly recognize content section headers\n */\nfunction checkForSectionHeader(\n node: SyntaxNode,\n point: Point,\n sourceMap: SourceMap,\n block: ParsedBlock,\n): SectionHeaderContext | null {\n const lineText = getLineTextFromBlock(block, point.row);\n if (!lineText) {\n return null;\n }\n\n // Check for section header pattern: optional indent + # + section name\n const match = lineText.match(/^(\\s*)#\\s*([A-Z][a-zA-Z0-9]*)/);\n if (!match) {\n return null;\n }\n\n const [fullMatch, indent, sectionName] = match;\n const hashStart = indent.length;\n const sectionStart = lineText.indexOf(sectionName, hashStart);\n const sectionEnd = sectionStart + sectionName.length;\n\n // Check if position is on the section name\n if (point.column >= hashStart && point.column <= sectionEnd) {\n // Find the entity context from the containing entry\n const entry = findContainingEntry(node);\n const entityContext = entry ? getEntityContextFromEntry(entry) : undefined;\n\n return {\n kind: \"section_header\",\n sectionName,\n location: {\n startIndex: 0,\n endIndex: 0,\n startPosition: { row: point.row, column: hashStart },\n endPosition: { row: point.row, column: fullMatch.length },\n },\n entityContext,\n sourceMap,\n };\n }\n\n return null;\n}\n\n/**\n * Classify a key node (metadata key)\n */\nfunction classifyKeyNode(node: SyntaxNode, sourceMap: SourceMap): MetadataKeyContext {\n const entry = findContainingEntry(node);\n const entityContext = entry ? getEntityContextFromEntry(entry) : undefined;\n\n return {\n kind: \"metadata_key\",\n key: extractKey(node).value,\n node: extractKey(node),\n entityContext,\n sourceMap,\n };\n}\n\n/**\n * Classify a field_name node in schema definitions\n */\nfunction classifyFieldNameNode(node: SyntaxNode, sourceMap: SourceMap): FieldNameContext {\n const entry = findContainingEntry(node);\n const entityContext = entry ? getEntityContextFromEntry(entry) : undefined;\n\n return {\n kind: \"field_name\",\n fieldName: extractFieldName(node).value,\n node: extractFieldName(node),\n entityContext,\n sourceMap,\n };\n}\n\n/**\n * Classify a section_name node in schema definitions\n */\nfunction classifySectionNameNode(node: SyntaxNode, sourceMap: SourceMap): SectionNameContext {\n const entry = findContainingEntry(node);\n const entityContext = entry ? getEntityContextFromEntry(entry) : undefined;\n\n return {\n kind: \"section_name\",\n sectionName: extractSectionName(node).value,\n node: extractSectionName(node),\n entityContext,\n sourceMap,\n };\n}\n\n/**\n * Classify an identifier node\n */\nfunction classifyIdentifierNode(node: SyntaxNode, sourceMap: SourceMap): NodeContext {\n const parent = node.parent;\n\n // Check if this is the entity (argument) in a data_entry with create/update directive\n if (parent?.type === \"data_entry\") {\n const directiveNode = parent.childForFieldName(\"directive\");\n const directive = directiveNode?.text;\n\n if (directive === \"create\" || directive === \"update\") {\n const argumentNode = parent.childForFieldName(\"argument\");\n if (argumentNode && argumentNode.id === node.id) {\n return {\n kind: \"entity\",\n entityName: node.text,\n location: extractLocation(node),\n sourceMap,\n };\n }\n }\n }\n\n // Check if this is the entity_name (argument) in a schema_entry\n if (parent?.type === \"schema_entry\") {\n const argumentNode = parent.childForFieldName(\"argument\");\n if (argumentNode && argumentNode.id === node.id) {\n return {\n kind: \"schema_entity\",\n entityName: node.text,\n node: extractIdentifier(node),\n sourceMap,\n };\n }\n }\n\n return { kind: \"unknown\" };\n}\n\n/**\n * Classify a markdown_header node (section in content)\n */\nfunction classifyMarkdownHeader(node: SyntaxNode, sourceMap: SourceMap): SectionHeaderContext {\n const entry = findContainingEntry(node);\n const entityContext = entry ? getEntityContextFromEntry(entry) : undefined;\n\n // Extract section name from \"# SectionName\" format\n const text = node.text.trim();\n const match = text.match(/^#\\s*([A-Z][a-zA-Z0-9]*)/);\n const sectionName = match ? match[1] : text.replace(/^#\\s*/, \"\");\n\n return {\n kind: \"section_header\",\n sectionName,\n location: extractLocation(node),\n entityContext,\n sourceMap,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,232 @@
1
+ //#region src/ast/visitor.ts
2
+ /**
3
+ * Get all child nodes of an AST node.
4
+ *
5
+ * This function knows the structure of each AST node type and returns
6
+ * all child nodes in a consistent order.
7
+ */
8
+ function getChildren(node) {
9
+ const children = [];
10
+ switch (node.type) {
11
+ case "source_file":
12
+ children.push(...node.entries);
13
+ break;
14
+ case "instance_entry": {
15
+ const n = node;
16
+ children.push(n.header);
17
+ children.push(...n.metadata);
18
+ if (n.content) children.push(n.content);
19
+ break;
20
+ }
21
+ case "schema_entry": {
22
+ const n = node;
23
+ children.push(n.header);
24
+ if (n.metadataBlock) children.push(n.metadataBlock);
25
+ if (n.sectionsBlock) children.push(n.sectionsBlock);
26
+ if (n.removeMetadataBlock) children.push(n.removeMetadataBlock);
27
+ if (n.removeSectionsBlock) children.push(n.removeSectionsBlock);
28
+ break;
29
+ }
30
+ case "synthesis_entry": {
31
+ const n = node;
32
+ children.push(n.header);
33
+ children.push(...n.metadata);
34
+ if (n.content) children.push(n.content);
35
+ break;
36
+ }
37
+ case "actualize_entry": {
38
+ const n = node;
39
+ children.push(n.header);
40
+ children.push(...n.metadata);
41
+ break;
42
+ }
43
+ case "instance_header": {
44
+ const n = node;
45
+ children.push(n.timestamp);
46
+ children.push(n.title);
47
+ if (n.link) children.push(n.link);
48
+ children.push(...n.tags);
49
+ break;
50
+ }
51
+ case "schema_header": {
52
+ const n = node;
53
+ children.push(n.timestamp);
54
+ children.push(n.entityName);
55
+ children.push(n.title);
56
+ if (n.link) children.push(n.link);
57
+ children.push(...n.tags);
58
+ break;
59
+ }
60
+ case "synthesis_header": {
61
+ const n = node;
62
+ children.push(n.timestamp);
63
+ children.push(n.title);
64
+ children.push(n.linkId);
65
+ children.push(...n.tags);
66
+ break;
67
+ }
68
+ case "actualize_header": {
69
+ const n = node;
70
+ children.push(n.timestamp);
71
+ children.push(n.target);
72
+ break;
73
+ }
74
+ case "metadata_block":
75
+ children.push(...node.fields);
76
+ break;
77
+ case "sections_block":
78
+ children.push(...node.sections);
79
+ break;
80
+ case "remove_metadata_block":
81
+ children.push(...node.fields);
82
+ break;
83
+ case "remove_sections_block":
84
+ children.push(...node.sections);
85
+ break;
86
+ case "field_definition": {
87
+ const n = node;
88
+ children.push(n.name);
89
+ children.push(n.typeExpr);
90
+ if (n.defaultValue) children.push(n.defaultValue);
91
+ if (n.description) children.push(n.description);
92
+ break;
93
+ }
94
+ case "field_removal": {
95
+ const n = node;
96
+ children.push(n.name);
97
+ if (n.reason) children.push(n.reason);
98
+ break;
99
+ }
100
+ case "section_definition": {
101
+ const n = node;
102
+ children.push(n.name);
103
+ if (n.description) children.push(n.description);
104
+ break;
105
+ }
106
+ case "section_removal": {
107
+ const n = node;
108
+ children.push(n.name);
109
+ if (n.reason) children.push(n.reason);
110
+ break;
111
+ }
112
+ case "array_type":
113
+ children.push(node.elementType);
114
+ break;
115
+ case "union_type":
116
+ children.push(...node.members);
117
+ break;
118
+ case "metadata": {
119
+ const n = node;
120
+ children.push(n.key);
121
+ children.push(n.value);
122
+ break;
123
+ }
124
+ case "content":
125
+ children.push(...node.children);
126
+ break;
127
+ case "timestamp": {
128
+ const n = node;
129
+ if (n.date) children.push(n.date);
130
+ if (n.time) children.push(n.time);
131
+ if (n.timezone) children.push(n.timezone);
132
+ break;
133
+ }
134
+ case "value": {
135
+ const n = node;
136
+ children.push(n.content);
137
+ break;
138
+ }
139
+ case "default_value": {
140
+ const n = node;
141
+ children.push(n.content);
142
+ break;
143
+ }
144
+ case "link_value":
145
+ children.push(node.link);
146
+ break;
147
+ case "query_value":
148
+ children.push(node.query);
149
+ break;
150
+ case "value_array":
151
+ children.push(...node.elements);
152
+ break;
153
+ case "query":
154
+ children.push(...node.conditions);
155
+ break;
156
+ case "primitive_type":
157
+ case "literal_type":
158
+ case "markdown_header":
159
+ case "content_line":
160
+ case "date_part":
161
+ case "time_part":
162
+ case "timezone_part":
163
+ case "title":
164
+ case "link":
165
+ case "tag":
166
+ case "identifier":
167
+ case "key":
168
+ case "field_name":
169
+ case "section_name":
170
+ case "description":
171
+ case "quoted_value":
172
+ case "datetime_value":
173
+ case "daterange":
174
+ case "number_value":
175
+ case "field_condition":
176
+ case "tag_condition":
177
+ case "link_condition":
178
+ case "syntax_error": break;
179
+ }
180
+ return children;
181
+ }
182
+ /**
183
+ * Walk an AST with a callback for each node.
184
+ *
185
+ * This is a simpler alternative to creating a visitor class.
186
+ *
187
+ * @param node - The root node to start traversal from
188
+ * @param callback - Called for each node in depth-first order
189
+ *
190
+ * @example
191
+ * forEachNode(sourceFile, (node) => {
192
+ * if (node.type === "syntax_error") {
193
+ * console.log("Found error:", node);
194
+ * }
195
+ * });
196
+ */
197
+ function forEachNode(node, callback) {
198
+ callback(node);
199
+ const children = getChildren(node);
200
+ for (const child of children) forEachNode(child, callback);
201
+ }
202
+ /**
203
+ * Collect all nodes of a specific type from an AST.
204
+ *
205
+ * @param node - The root node to search from
206
+ * @param type - The node type to collect (e.g., "syntax_error", "link")
207
+ * @returns Array of nodes matching the type
208
+ *
209
+ * @example
210
+ * const links = collectNodes(sourceFile, "link");
211
+ * const errors = collectNodes(sourceFile, "syntax_error");
212
+ */
213
+ function collectNodes(root, type) {
214
+ const results = [];
215
+ forEachNode(root, (node) => {
216
+ if (node.type === type) results.push(node);
217
+ });
218
+ return results;
219
+ }
220
+ /**
221
+ * Collect all syntax errors from an AST.
222
+ *
223
+ * @param root - The root node to search from
224
+ * @returns Array of SyntaxErrorNode instances
225
+ */
226
+ function collectSyntaxErrors(root) {
227
+ return collectNodes(root, "syntax_error");
228
+ }
229
+
230
+ //#endregion
231
+ export { collectSyntaxErrors };
232
+ //# sourceMappingURL=visitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visitor.js","names":["children: AstNode[]","results: N[]"],"sources":["../../src/ast/visitor.ts"],"sourcesContent":["/**\n * AST Visitor Infrastructure\n *\n * Provides a visitor pattern for traversing and processing AST nodes.\n * Supports both void visitors (for side effects) and value-returning visitors.\n *\n * @example\n * // Collecting all syntax errors from an AST\n * class ErrorCollector extends BaseVisitor<void> {\n * errors: SyntaxErrorNode[] = [];\n *\n * visitSyntaxError(node: SyntaxErrorNode): void {\n * this.errors.push(node);\n * }\n * }\n *\n * const collector = new ErrorCollector();\n * walkAst(ast, collector);\n * console.log(collector.errors);\n */\n\nimport type {\n AstNode,\n SourceFile,\n Entry,\n InstanceEntry,\n InstanceHeader,\n SchemaEntry,\n SchemaHeader,\n SynthesisEntry,\n SynthesisHeader,\n ActualizeEntry,\n ActualizeHeader,\n MetadataBlock,\n SectionsBlock,\n RemoveMetadataBlock,\n RemoveSectionsBlock,\n FieldDefinition,\n FieldRemoval,\n SectionDefinition,\n SectionRemoval,\n TypeExpression,\n PrimitiveType,\n LiteralType,\n ArrayType,\n UnionType,\n Metadata,\n Content,\n MarkdownHeader,\n ContentLine,\n DatePart,\n TimePart,\n TimezonePart,\n Timestamp,\n Title,\n Link,\n Tag,\n Identifier,\n Key,\n Value,\n QuotedValue,\n LinkValue,\n DatetimeValue,\n DaterangeValue,\n NumberValue,\n QueryValue,\n ValueArray,\n Query,\n FieldCondition,\n TagCondition,\n LinkCondition,\n FieldName,\n SectionName,\n Description,\n DefaultValue,\n SyntaxErrorNode,\n} from \"./ast-types.js\";\n\n/**\n * Visitor interface with optional methods for each AST node type.\n *\n * Implement only the methods you need - unimplemented methods will\n * fall through to visitDefault (which visits children by default).\n */\nexport interface AstVisitor<T = void> {\n /**\n * Main entry point - dispatches to the appropriate visit method\n */\n visit(node: AstNode): T;\n\n // Core nodes\n visitSourceFile?(node: SourceFile): T;\n visitEntry?(node: Entry): T;\n\n // Entry types\n visitInstanceEntry?(node: InstanceEntry): T;\n visitSchemaEntry?(node: SchemaEntry): T;\n visitSynthesisEntry?(node: SynthesisEntry): T;\n visitActualizeEntry?(node: ActualizeEntry): T;\n\n // Header types\n visitInstanceHeader?(node: InstanceHeader): T;\n visitSchemaHeader?(node: SchemaHeader): T;\n visitSynthesisHeader?(node: SynthesisHeader): T;\n visitActualizeHeader?(node: ActualizeHeader): T;\n\n // Schema blocks\n visitMetadataBlock?(node: MetadataBlock): T;\n visitSectionsBlock?(node: SectionsBlock): T;\n visitRemoveMetadataBlock?(node: RemoveMetadataBlock): T;\n visitRemoveSectionsBlock?(node: RemoveSectionsBlock): T;\n\n // Field and section definitions\n visitFieldDefinition?(node: FieldDefinition): T;\n visitFieldRemoval?(node: FieldRemoval): T;\n visitSectionDefinition?(node: SectionDefinition): T;\n visitSectionRemoval?(node: SectionRemoval): T;\n\n // Type expressions\n visitTypeExpression?(node: TypeExpression): T;\n visitPrimitiveType?(node: PrimitiveType): T;\n visitLiteralType?(node: LiteralType): T;\n visitArrayType?(node: ArrayType): T;\n visitUnionType?(node: UnionType): T;\n\n // Instance entry components\n visitMetadata?(node: Metadata): T;\n visitContent?(node: Content): T;\n visitMarkdownHeader?(node: MarkdownHeader): T;\n visitContentLine?(node: ContentLine): T;\n\n // Timestamp parts\n visitDatePart?(node: DatePart): T;\n visitTimePart?(node: TimePart): T;\n visitTimezonePart?(node: TimezonePart): T;\n visitTimestamp?(node: Timestamp): T;\n\n // Terminal nodes\n visitTitle?(node: Title): T;\n visitLink?(node: Link): T;\n visitTag?(node: Tag): T;\n visitIdentifier?(node: Identifier): T;\n visitKey?(node: Key): T;\n visitValue?(node: Value): T;\n visitFieldName?(node: FieldName): T;\n visitSectionName?(node: SectionName): T;\n visitDescription?(node: Description): T;\n visitDefaultValue?(node: DefaultValue): T;\n\n // Value types\n visitQuotedValue?(node: QuotedValue): T;\n visitLinkValue?(node: LinkValue): T;\n visitDatetimeValue?(node: DatetimeValue): T;\n visitDaterangeValue?(node: DaterangeValue): T;\n visitNumberValue?(node: NumberValue): T;\n visitQueryValue?(node: QueryValue): T;\n visitValueArray?(node: ValueArray): T;\n\n // Query types\n visitQuery?(node: Query): T;\n visitFieldCondition?(node: FieldCondition): T;\n visitTagCondition?(node: TagCondition): T;\n visitLinkCondition?(node: LinkCondition): T;\n\n // Error nodes\n visitSyntaxError?(node: SyntaxErrorNode): T;\n}\n\n/**\n * Convert a snake_case type name to PascalCase method name\n * e.g., \"source_file\" -> \"SourceFile\"\n */\nfunction toPascalCase(type: string): string {\n return type\n .split(\"_\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n}\n\n/**\n * Get the visitor method name for a node type\n * e.g., \"source_file\" -> \"visitSourceFile\"\n */\nfunction getVisitorMethodName(type: string): string {\n return `visit${toPascalCase(type)}`;\n}\n\n/**\n * Get all child nodes of an AST node.\n *\n * This function knows the structure of each AST node type and returns\n * all child nodes in a consistent order.\n */\nexport function getChildren(node: AstNode): AstNode[] {\n const children: AstNode[] = [];\n\n switch (node.type) {\n case \"source_file\":\n children.push(...(node as SourceFile).entries);\n break;\n\n case \"instance_entry\": {\n const n = node as InstanceEntry;\n children.push(n.header);\n children.push(...n.metadata);\n if (n.content) {\n children.push(n.content);\n }\n break;\n }\n\n case \"schema_entry\": {\n const n = node as SchemaEntry;\n children.push(n.header);\n if (n.metadataBlock) {\n children.push(n.metadataBlock);\n }\n if (n.sectionsBlock) {\n children.push(n.sectionsBlock);\n }\n if (n.removeMetadataBlock) {\n children.push(n.removeMetadataBlock);\n }\n if (n.removeSectionsBlock) {\n children.push(n.removeSectionsBlock);\n }\n break;\n }\n\n case \"synthesis_entry\": {\n const n = node as SynthesisEntry;\n children.push(n.header);\n children.push(...n.metadata);\n if (n.content) {\n children.push(n.content);\n }\n break;\n }\n\n case \"actualize_entry\": {\n const n = node as ActualizeEntry;\n children.push(n.header);\n children.push(...n.metadata);\n break;\n }\n\n case \"instance_header\": {\n const n = node as InstanceHeader;\n children.push(n.timestamp);\n children.push(n.title);\n if (n.link) {\n children.push(n.link);\n }\n children.push(...n.tags);\n break;\n }\n\n case \"schema_header\": {\n const n = node as SchemaHeader;\n children.push(n.timestamp);\n children.push(n.entityName);\n children.push(n.title);\n if (n.link) {\n children.push(n.link);\n }\n children.push(...n.tags);\n break;\n }\n\n case \"synthesis_header\": {\n const n = node as SynthesisHeader;\n children.push(n.timestamp);\n children.push(n.title);\n children.push(n.linkId);\n children.push(...n.tags);\n break;\n }\n\n case \"actualize_header\": {\n const n = node as ActualizeHeader;\n children.push(n.timestamp);\n children.push(n.target);\n break;\n }\n\n case \"metadata_block\":\n children.push(...(node as MetadataBlock).fields);\n break;\n\n case \"sections_block\":\n children.push(...(node as SectionsBlock).sections);\n break;\n\n case \"remove_metadata_block\":\n children.push(...(node as RemoveMetadataBlock).fields);\n break;\n\n case \"remove_sections_block\":\n children.push(...(node as RemoveSectionsBlock).sections);\n break;\n\n case \"field_definition\": {\n const n = node as FieldDefinition;\n children.push(n.name);\n children.push(n.typeExpr);\n if (n.defaultValue) {\n children.push(n.defaultValue);\n }\n if (n.description) {\n children.push(n.description);\n }\n break;\n }\n\n case \"field_removal\": {\n const n = node as FieldRemoval;\n children.push(n.name);\n if (n.reason) {\n children.push(n.reason);\n }\n break;\n }\n\n case \"section_definition\": {\n const n = node as SectionDefinition;\n children.push(n.name);\n if (n.description) {\n children.push(n.description);\n }\n break;\n }\n\n case \"section_removal\": {\n const n = node as SectionRemoval;\n children.push(n.name);\n if (n.reason) {\n children.push(n.reason);\n }\n break;\n }\n\n case \"array_type\":\n children.push((node as ArrayType).elementType);\n break;\n\n case \"union_type\":\n children.push(...(node as UnionType).members);\n break;\n\n case \"metadata\": {\n const n = node as Metadata;\n children.push(n.key);\n children.push(n.value);\n break;\n }\n\n case \"content\":\n children.push(...(node as Content).children);\n break;\n\n case \"timestamp\": {\n const n = node as Timestamp;\n if (n.date) {\n children.push(n.date);\n }\n if (n.time) {\n children.push(n.time);\n }\n if (n.timezone) {\n children.push(n.timezone);\n }\n break;\n }\n\n case \"value\": {\n const n = node as Value;\n children.push(n.content);\n break;\n }\n\n case \"default_value\": {\n const n = node as DefaultValue;\n children.push(n.content);\n break;\n }\n\n case \"link_value\":\n children.push((node as LinkValue).link);\n break;\n\n case \"query_value\":\n children.push((node as QueryValue).query);\n break;\n\n case \"value_array\":\n children.push(...(node as ValueArray).elements);\n break;\n\n case \"query\":\n children.push(...(node as Query).conditions);\n break;\n\n // Terminal nodes - no children\n case \"primitive_type\":\n case \"literal_type\":\n case \"markdown_header\":\n case \"content_line\":\n case \"date_part\":\n case \"time_part\":\n case \"timezone_part\":\n case \"title\":\n case \"link\":\n case \"tag\":\n case \"identifier\":\n case \"key\":\n case \"field_name\":\n case \"section_name\":\n case \"description\":\n case \"quoted_value\":\n case \"datetime_value\":\n case \"daterange\":\n case \"number_value\":\n case \"field_condition\":\n case \"tag_condition\":\n case \"link_condition\":\n case \"syntax_error\":\n // No children\n break;\n }\n\n return children;\n}\n\n/**\n * Abstract base visitor with default dispatch behavior.\n *\n * Subclasses can override specific visit methods and call super.visit()\n * to continue traversal to children.\n *\n * @example\n * class CountNodes extends BaseVisitor<void> {\n * count = 0;\n *\n * protected visitDefault(node: AstNode): void {\n * this.count++;\n * super.visitDefault(node); // Continue to children\n * }\n * }\n */\nexport abstract class BaseVisitor<T = void> implements AstVisitor<T> {\n /**\n * Main dispatch method - routes to the appropriate visit method based on node type.\n */\n visit(node: AstNode): T {\n const methodName = getVisitorMethodName(node.type);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const method = (this as any)[methodName];\n\n if (typeof method === \"function\") {\n return method.call(this, node);\n }\n\n return this.visitDefault(node);\n }\n\n /**\n * Default handler for nodes without a specific visit method.\n * By default, visits all children.\n *\n * Override this to change the default behavior.\n */\n protected visitDefault(node: AstNode): T {\n const children = getChildren(node);\n for (const child of children) {\n this.visit(child);\n }\n return undefined as T;\n }\n}\n\n/**\n * Walk an AST with a visitor, starting from the given node.\n *\n * This is a convenience function that calls visitor.visit(node).\n *\n * @param node - The root node to start traversal from\n * @param visitor - The visitor to use for traversal\n * @returns The result of visiting the root node\n *\n * @example\n * const errorCollector = new ErrorCollector();\n * walkAst(sourceFile, errorCollector);\n */\nexport function walkAst<T>(node: AstNode, visitor: AstVisitor<T>): T {\n return visitor.visit(node);\n}\n\n/**\n * Walk an AST with a callback for each node.\n *\n * This is a simpler alternative to creating a visitor class.\n *\n * @param node - The root node to start traversal from\n * @param callback - Called for each node in depth-first order\n *\n * @example\n * forEachNode(sourceFile, (node) => {\n * if (node.type === \"syntax_error\") {\n * console.log(\"Found error:\", node);\n * }\n * });\n */\nexport function forEachNode(node: AstNode, callback: (node: AstNode) => void): void {\n callback(node);\n const children = getChildren(node);\n for (const child of children) {\n forEachNode(child, callback);\n }\n}\n\n/**\n * Collect all nodes of a specific type from an AST.\n *\n * @param node - The root node to search from\n * @param type - The node type to collect (e.g., \"syntax_error\", \"link\")\n * @returns Array of nodes matching the type\n *\n * @example\n * const links = collectNodes(sourceFile, \"link\");\n * const errors = collectNodes(sourceFile, \"syntax_error\");\n */\nexport function collectNodes<N extends AstNode>(root: AstNode, type: string): N[] {\n const results: N[] = [];\n forEachNode(root, (node) => {\n if (node.type === type) {\n results.push(node as N);\n }\n });\n return results;\n}\n\n/**\n * Collect all syntax errors from an AST.\n *\n * @param root - The root node to search from\n * @returns Array of SyntaxErrorNode instances\n */\nexport function collectSyntaxErrors(root: AstNode): SyntaxErrorNode[] {\n return collectNodes<SyntaxErrorNode>(root, \"syntax_error\");\n}\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { Location } from "../ast/ast-types.js";
2
+ import { SourceMap } from "../source-map.js";
3
+ import { Workspace } from "../model/workspace.js";
4
+ import { Severity } from "./rules/rules.js";
5
+
6
+ //#region src/checker/check.d.ts
7
+
8
+ /**
9
+ * A diagnostic message from the checker
10
+ */
11
+ interface Diagnostic {
12
+ /** The rule code that generated this diagnostic */
13
+ code: string;
14
+ /** Severity level */
15
+ severity: Exclude<Severity, "off">;
16
+ /** Human-readable message */
17
+ message: string;
18
+ /** File path where the issue was found */
19
+ file: string;
20
+ /** Location in the source (file-absolute) */
21
+ location: Location;
22
+ /** Additional data for the diagnostic (rule-specific) */
23
+ data?: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * Partial diagnostic with optional sourceMap for position mapping
27
+ */
28
+ interface PartialDiagnostic {
29
+ /** Human-readable message */
30
+ message: string;
31
+ /** File path where the issue was found */
32
+ file: string;
33
+ /** Location in the source (block-relative) */
34
+ location: Location;
35
+ /** Source map for converting block-relative to file-absolute positions */
36
+ sourceMap?: SourceMap;
37
+ /** Additional data for the diagnostic (rule-specific) */
38
+ data?: Record<string, unknown>;
39
+ }
40
+ /**
41
+ * Configuration for the checker
42
+ */
43
+ interface CheckConfig {
44
+ /** Override severity for specific rules */
45
+ rules?: Partial<Record<string, Severity>>;
46
+ }
47
+ /**
48
+ * Check a single document for issues (alias for checkModel that gets model from workspace)
49
+ */
50
+ declare function checkDocument(file: string, workspace: Workspace, config?: CheckConfig): Diagnostic[];
51
+ //#endregion
52
+ export { CheckConfig, Diagnostic, PartialDiagnostic, checkDocument };
53
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","names":[],"sources":["../../src/checker/check.ts"],"sourcesContent":[],"mappings":";;;;;;;AAeA;;;AAUY,UAVK,UAAA,CAUL;EAEH;EAAM,IAAA,EAAA,MAAA;EAME;EAML,QAAA,EApBA,OAoBA,CApBQ,QAoBR,EAAA,KAAA,CAAA;EAEE;EAEL,OAAA,EAAA,MAAA;EAAM;EAME,IAAA,EAAA,MAAA;EAEgB;EAAf,QAAA,EA1BN,QA0BM;EAAR;EAAO,IAAA,CAAA,EAxBR,MAwBQ,CAAA,MAAA,EAAA,OAAA,CAAA;AAkGjB;;;;AAIa,UAxHI,iBAAA,CAwHJ;;;;;;YAlHD;;cAEE;;SAEL;;;;;UAMQ,WAAA;;UAEP,QAAQ,eAAe;;;;;iBAkGjB,aAAA,0BAEH,oBACH,cACP"}
@@ -0,0 +1,105 @@
1
+ import { toFileLocation } from "../source-map.js";
2
+ import { allRules } from "./rules/rules.js";
3
+ import { collectSyntaxErrors } from "../ast/visitor.js";
4
+ import { buildWorkspaceIndex } from "./workspace-index.js";
5
+ import { runVisitors, runVisitorsOnModel } from "./visitor.js";
6
+
7
+ //#region src/checker/check.ts
8
+ /**
9
+ * Get the effective severity for a rule given a config
10
+ */
11
+ function getEffectiveSeverity(rule, config) {
12
+ return config.rules?.[rule.code] ?? rule.defaultSeverity;
13
+ }
14
+ const noop = () => {};
15
+ /**
16
+ * Collect syntax errors from an AST and convert them to diagnostics.
17
+ * Includes both root-level parse errors and nested syntax errors within entries.
18
+ */
19
+ function collectSyntaxErrors$1(ast, file, sourceMap) {
20
+ const diagnostics = [];
21
+ for (const error of ast.syntaxErrors) diagnostics.push({
22
+ code: `syntax-${error.code}`,
23
+ severity: "error",
24
+ message: error.message,
25
+ file,
26
+ location: sourceMap ? toFileLocation(sourceMap, error.location) : error.location
27
+ });
28
+ const nestedErrors = collectSyntaxErrors(ast);
29
+ for (const error of nestedErrors) diagnostics.push({
30
+ code: `syntax-${error.code}`,
31
+ severity: "error",
32
+ message: error.message,
33
+ file,
34
+ location: sourceMap ? toFileLocation(sourceMap, error.location) : error.location
35
+ });
36
+ return diagnostics;
37
+ }
38
+ /**
39
+ * Check a workspace for issues using all configured rules
40
+ */
41
+ function check(workspace, config = {}) {
42
+ const diagnostics = [];
43
+ for (const model of workspace.allModels()) diagnostics.push(...collectSyntaxErrors$1(model.ast, model.file, model.sourceMap));
44
+ const index = buildWorkspaceIndex(workspace);
45
+ runVisitors(allRules.filter((r) => getEffectiveSeverity(r, config) !== "off").map((rule) => createTrackedVisitor(rule, config, diagnostics)), workspace, index, noop);
46
+ return diagnostics;
47
+ }
48
+ /**
49
+ * Check a single model for issues
50
+ */
51
+ function checkModel(model, workspace, config = {}) {
52
+ const diagnostics = [];
53
+ diagnostics.push(...collectSyntaxErrors$1(model.ast, model.file, model.sourceMap));
54
+ const index = buildWorkspaceIndex(workspace);
55
+ runVisitorsOnModel(allRules.filter((r) => getEffectiveSeverity(r, config) !== "off").map((rule) => createTrackedVisitor(rule, config, diagnostics)), model, workspace, index, noop);
56
+ return diagnostics.filter((d) => d.file === model.file);
57
+ }
58
+ /**
59
+ * Check a single document for issues (alias for checkModel that gets model from workspace)
60
+ */
61
+ function checkDocument(file, workspace, config = {}) {
62
+ const model = workspace.getModel(file);
63
+ if (!model) return [];
64
+ return checkModel(model, workspace, config);
65
+ }
66
+ /**
67
+ * Create a visitor that tracks diagnostics for a specific rule.
68
+ * Assumes the rule has already been filtered to ensure severity !== "off".
69
+ */
70
+ function createTrackedVisitor(rule, config, diagnostics) {
71
+ const severity = getEffectiveSeverity(rule, config);
72
+ const v = rule.visitor;
73
+ const wrapCtx = (ctx) => ({
74
+ ...ctx,
75
+ report: (partial) => {
76
+ diagnostics.push(createDiagnostic(partial, rule.code, severity));
77
+ }
78
+ });
79
+ return {
80
+ beforeCheck: v.beforeCheck ? (ctx) => v.beforeCheck(wrapCtx(ctx)) : void 0,
81
+ visitInstanceEntry: v.visitInstanceEntry ? (entry, ctx) => v.visitInstanceEntry(entry, wrapCtx(ctx)) : void 0,
82
+ visitSchemaEntry: v.visitSchemaEntry ? (entry, ctx) => v.visitSchemaEntry(entry, wrapCtx(ctx)) : void 0,
83
+ visitSynthesisEntry: v.visitSynthesisEntry ? (entry, ctx) => v.visitSynthesisEntry(entry, wrapCtx(ctx)) : void 0,
84
+ visitActualizeEntry: v.visitActualizeEntry ? (entry, ctx) => v.visitActualizeEntry(entry, wrapCtx(ctx)) : void 0,
85
+ afterCheck: v.afterCheck ? (ctx) => v.afterCheck(wrapCtx(ctx)) : void 0
86
+ };
87
+ }
88
+ /**
89
+ * Convert a partial diagnostic to a full diagnostic.
90
+ */
91
+ function createDiagnostic(partial, code, severity) {
92
+ const location = partial.sourceMap ? toFileLocation(partial.sourceMap, partial.location) : partial.location;
93
+ return {
94
+ code,
95
+ severity,
96
+ message: partial.message,
97
+ file: partial.file,
98
+ location,
99
+ data: partial.data
100
+ };
101
+ }
102
+
103
+ //#endregion
104
+ export { check, checkDocument };
105
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","names":["collectSyntaxErrors","diagnostics: Diagnostic[]","collectSyntaxErrorNodes"],"sources":["../../src/checker/check.ts"],"sourcesContent":["import type { Workspace, InvalidationResult } from \"../model/workspace.js\";\nimport type { SemanticModel } from \"../semantic/analyzer.js\";\nimport type { Entry, SourceFile } from \"../ast/ast-types.js\";\nimport type { Rule, Severity } from \"./rules/rules.js\";\nimport { allRules } from \"./rules/rules.js\";\nimport { toFileLocation, type SourceMap } from \"../source-map.js\";\nimport { collectSyntaxErrors as collectSyntaxErrorNodes } from \"../ast/visitor.js\";\nimport { buildWorkspaceIndex } from \"./workspace-index.js\";\nimport { runVisitors, runVisitorsOnModel, runVisitorsOnEntries } from \"./visitor.js\";\nimport type { RuleVisitor } from \"./visitor.js\";\nimport type { Location } from \"../ast/ast-types.js\";\n\n/**\n * A diagnostic message from the checker\n */\nexport interface Diagnostic {\n /** The rule code that generated this diagnostic */\n code: string;\n /** Severity level */\n severity: Exclude<Severity, \"off\">;\n /** Human-readable message */\n message: string;\n /** File path where the issue was found */\n file: string;\n /** Location in the source (file-absolute) */\n location: Location;\n /** Additional data for the diagnostic (rule-specific) */\n data?: Record<string, unknown>;\n}\n\n/**\n * Partial diagnostic with optional sourceMap for position mapping\n */\nexport interface PartialDiagnostic {\n /** Human-readable message */\n message: string;\n /** File path where the issue was found */\n file: string;\n /** Location in the source (block-relative) */\n location: Location;\n /** Source map for converting block-relative to file-absolute positions */\n sourceMap?: SourceMap;\n /** Additional data for the diagnostic (rule-specific) */\n data?: Record<string, unknown>;\n}\n\n/**\n * Configuration for the checker\n */\nexport interface CheckConfig {\n /** Override severity for specific rules */\n rules?: Partial<Record<string, Severity>>;\n}\n\n/**\n * Get the effective severity for a rule given a config\n */\nexport function getEffectiveSeverity(rule: Rule, config: CheckConfig): Severity {\n return config.rules?.[rule.code] ?? rule.defaultSeverity;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nconst noop = () => {};\n\n/**\n * Collect syntax errors from an AST and convert them to diagnostics.\n * Includes both root-level parse errors and nested syntax errors within entries.\n */\nfunction collectSyntaxErrors(ast: SourceFile, file: string, sourceMap?: SourceMap): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n // Collect root-level syntax errors (malformed entries)\n for (const error of ast.syntaxErrors) {\n diagnostics.push({\n code: `syntax-${error.code}`,\n severity: \"error\" as const,\n message: error.message,\n file,\n location: sourceMap ? toFileLocation(sourceMap, error.location) : error.location,\n });\n }\n\n // Collect nested syntax errors within valid entries\n const nestedErrors = collectSyntaxErrorNodes(ast);\n for (const error of nestedErrors) {\n diagnostics.push({\n code: `syntax-${error.code}`,\n severity: \"error\" as const,\n message: error.message,\n file,\n location: sourceMap ? toFileLocation(sourceMap, error.location) : error.location,\n });\n }\n\n return diagnostics;\n}\n\n/**\n * Check a workspace for issues using all configured rules\n */\nexport function check(workspace: Workspace, config: CheckConfig = {}): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n // Collect syntax errors from all models\n for (const model of workspace.allModels()) {\n diagnostics.push(...collectSyntaxErrors(model.ast, model.file, model.sourceMap));\n }\n\n // Build workspace index for efficient rule execution\n const index = buildWorkspaceIndex(workspace);\n\n // Get active rules and their visitors\n const activeRules = allRules.filter((r) => getEffectiveSeverity(r, config) !== \"off\");\n const visitors = activeRules.map((rule) => createTrackedVisitor(rule, config, diagnostics));\n\n // Run all visitors in a single pass (report handled by wrapped visitors)\n runVisitors(visitors, workspace, index, noop);\n\n return diagnostics;\n}\n\n/**\n * Check a single model for issues\n */\nexport function checkModel(\n model: SemanticModel,\n workspace: Workspace,\n config: CheckConfig = {},\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n diagnostics.push(...collectSyntaxErrors(model.ast, model.file, model.sourceMap));\n\n // Build workspace index\n const index = buildWorkspaceIndex(workspace);\n\n // Get active rules and their visitors\n const activeRules = allRules.filter((r) => getEffectiveSeverity(r, config) !== \"off\");\n const visitors = activeRules.map((rule) => createTrackedVisitor(rule, config, diagnostics));\n\n // Run visitors on this model only\n runVisitorsOnModel(visitors, model, workspace, index, noop);\n\n return diagnostics.filter((d) => d.file === model.file);\n}\n\n/**\n * Check a single document for issues (alias for checkModel that gets model from workspace)\n */\nexport function checkDocument(\n file: string,\n workspace: Workspace,\n config: CheckConfig = {},\n): Diagnostic[] {\n const model = workspace.getModel(file);\n if (!model) {\n return [];\n }\n return checkModel(model, workspace, config);\n}\n\n/**\n * Perform incremental checking based on what changed.\n *\n * Only runs rules that are affected by the changes, and only on\n * the affected scope (entry, document, or workspace).\n */\nexport function checkIncremental(\n workspace: Workspace,\n changedFile: string,\n changedEntries: Entry[],\n invalidation: InvalidationResult,\n config: CheckConfig = {},\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n const model = workspace.getModel(changedFile);\n if (!model) {\n return diagnostics;\n }\n\n // Always collect syntax errors\n diagnostics.push(...collectSyntaxErrors(model.ast, model.file, model.sourceMap));\n\n // Build index\n const index = buildWorkspaceIndex(workspace);\n\n // Partition rules by scope and dependencies\n const { entryRules, documentRules, workspaceRules } = partitionRulesByScope(\n allRules,\n config,\n invalidation,\n );\n\n // Run entry-scoped rules on changed entries only\n if (entryRules.length > 0 && changedEntries.length > 0) {\n const visitors = entryRules.map((rule) => createTrackedVisitor(rule, config, diagnostics));\n runVisitorsOnEntries(visitors, changedEntries, model, workspace, index, noop);\n }\n\n // Run document-scoped rules on the full document\n if (documentRules.length > 0) {\n const visitors = documentRules.map((rule) => createTrackedVisitor(rule, config, diagnostics));\n runVisitorsOnModel(visitors, model, workspace, index, noop);\n }\n\n // Run workspace-scoped rules\n if (workspaceRules.length > 0) {\n const visitors = workspaceRules.map((rule) => createTrackedVisitor(rule, config, diagnostics));\n runVisitors(visitors, workspace, index, noop);\n }\n\n // Filter to only diagnostics for the changed file\n return diagnostics.filter((d) => d.file === changedFile);\n}\n\n/**\n * Create a visitor that tracks diagnostics for a specific rule.\n * Assumes the rule has already been filtered to ensure severity !== \"off\".\n */\nfunction createTrackedVisitor(\n rule: Rule,\n config: CheckConfig,\n diagnostics: Diagnostic[],\n): RuleVisitor {\n // Safe: callers filter out rules with severity === \"off\" before calling this function\n const severity = getEffectiveSeverity(rule, config) as Exclude<Severity, \"off\">;\n const v = rule.visitor;\n\n const wrapCtx = <T extends object>(ctx: T) => ({\n ...ctx,\n report: (partial: PartialDiagnostic) => {\n diagnostics.push(createDiagnostic(partial, rule.code, severity));\n },\n });\n\n return {\n beforeCheck: v.beforeCheck ? (ctx) => v.beforeCheck!(wrapCtx(ctx)) : undefined,\n visitInstanceEntry: v.visitInstanceEntry\n ? (entry, ctx) => v.visitInstanceEntry!(entry, wrapCtx(ctx))\n : undefined,\n visitSchemaEntry: v.visitSchemaEntry\n ? (entry, ctx) => v.visitSchemaEntry!(entry, wrapCtx(ctx))\n : undefined,\n visitSynthesisEntry: v.visitSynthesisEntry\n ? (entry, ctx) => v.visitSynthesisEntry!(entry, wrapCtx(ctx))\n : undefined,\n visitActualizeEntry: v.visitActualizeEntry\n ? (entry, ctx) => v.visitActualizeEntry!(entry, wrapCtx(ctx))\n : undefined,\n afterCheck: v.afterCheck ? (ctx) => v.afterCheck!(wrapCtx(ctx)) : undefined,\n };\n}\n\n/**\n * Partition rules by scope, filtering based on invalidation.\n */\nfunction partitionRulesByScope(\n rules: Rule[],\n config: CheckConfig,\n invalidation: InvalidationResult,\n): { entryRules: Rule[]; documentRules: Rule[]; workspaceRules: Rule[] } {\n const entryRules: Rule[] = [];\n const documentRules: Rule[] = [];\n const workspaceRules: Rule[] = [];\n\n for (const rule of rules) {\n const severity = getEffectiveSeverity(rule, config);\n if (severity === \"off\") {\n continue;\n }\n\n const deps = rule.dependencies;\n const scope = deps.scope;\n\n if (scope === \"workspace\") {\n // Only run if relevant dependencies changed\n if (deps.schemas && invalidation.schemasChanged) {\n workspaceRules.push(rule);\n } else if (deps.links && invalidation.linksChanged) {\n workspaceRules.push(rule);\n } else if (!deps.schemas && !deps.links) {\n workspaceRules.push(rule);\n }\n } else if (scope === \"document\") {\n documentRules.push(rule);\n } else {\n entryRules.push(rule);\n }\n }\n\n return { entryRules, documentRules, workspaceRules };\n}\n\n/**\n * Convert a partial diagnostic to a full diagnostic.\n */\nfunction createDiagnostic(\n partial: PartialDiagnostic,\n code: string,\n severity: Exclude<Severity, \"off\">,\n): Diagnostic {\n const location = partial.sourceMap\n ? toFileLocation(partial.sourceMap, partial.location)\n : partial.location;\n\n return {\n code,\n severity,\n message: partial.message,\n file: partial.file,\n location,\n data: partial.data,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { formatTimestamp } from "../../formatters.js";
2
+
3
+ //#region src/checker/rules/actualize-missing-updated.ts
4
+ const category = "metadata";
5
+ const visitor = { visitActualizeEntry(entry, ctx) {
6
+ const target = entry.header.target.id;
7
+ const timestamp = formatTimestamp(entry.header.timestamp);
8
+ if (!entry.metadata.find((m) => m.key.value === "checkpoint")) ctx.report({
9
+ message: `Actualize entry is missing checkpoint. Add 'checkpoint: "ts:${timestamp}"' or 'checkpoint: "git:<hash>"' to track when this synthesis was last run.`,
10
+ file: ctx.file,
11
+ location: entry.location,
12
+ sourceMap: ctx.sourceMap,
13
+ data: {
14
+ target,
15
+ suggestedTimestamp: timestamp
16
+ }
17
+ });
18
+ } };
19
+ /**
20
+ * Check that actualize-synthesis entries have a checkpoint field for tracking
21
+ */
22
+ const actualizeMissingUpdatedRule = {
23
+ code: "actualize-missing-updated",
24
+ name: "Actualize Missing Checkpoint",
25
+ description: "An actualize-synthesis entry must have a 'checkpoint:' field for tracking",
26
+ category,
27
+ defaultSeverity: "error",
28
+ dependencies: { scope: "entry" },
29
+ visitor
30
+ };
31
+
32
+ //#endregion
33
+ export { actualizeMissingUpdatedRule };
34
+ //# sourceMappingURL=actualize-missing-updated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actualize-missing-updated.js","names":["category: RuleCategory","visitor: RuleVisitor","actualizeMissingUpdatedRule: Rule"],"sources":["../../../src/checker/rules/actualize-missing-updated.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\nimport { formatTimestamp } from \"../../formatters.js\";\n\nconst category: RuleCategory = \"metadata\";\n\nconst visitor: RuleVisitor = {\n visitActualizeEntry(entry, ctx) {\n const target = entry.header.target.id;\n const timestamp = formatTimestamp(entry.header.timestamp);\n const checkpointField = entry.metadata.find((m) => m.key.value === \"checkpoint\");\n\n if (!checkpointField) {\n ctx.report({\n message: `Actualize entry is missing checkpoint. Add 'checkpoint: \"ts:${timestamp}\"' or 'checkpoint: \"git:<hash>\"' to track when this synthesis was last run.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { target, suggestedTimestamp: timestamp },\n });\n }\n },\n};\n\n/**\n * Check that actualize-synthesis entries have a checkpoint field for tracking\n */\nexport const actualizeMissingUpdatedRule: Rule = {\n code: \"actualize-missing-updated\",\n name: \"Actualize Missing Checkpoint\",\n description: \"An actualize-synthesis entry must have a 'checkpoint:' field for tracking\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\" },\n visitor,\n};\n"],"mappings":";;;AAIA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,oBAAoB,OAAO,KAAK;CAC9B,MAAM,SAAS,MAAM,OAAO,OAAO;CACnC,MAAM,YAAY,gBAAgB,MAAM,OAAO,UAAU;AAGzD,KAAI,CAFoB,MAAM,SAAS,MAAM,MAAM,EAAE,IAAI,UAAU,aAAa,CAG9E,KAAI,OAAO;EACT,SAAS,+DAA+D,UAAU;EAClF,MAAM,IAAI;EACV,UAAU,MAAM;EAChB,WAAW,IAAI;EACf,MAAM;GAAE;GAAQ,oBAAoB;GAAW;EAChD,CAAC;GAGP;;;;AAKD,MAAaC,8BAAoC;CAC/C,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc,EAAE,OAAO,SAAS;CAChC;CACD"}
@@ -0,0 +1,42 @@
1
+ //#region src/checker/rules/actualize-unresolved-target.ts
2
+ const category = "link";
3
+ const visitor = { visitActualizeEntry(entry, ctx) {
4
+ const targetId = entry.header.target.id;
5
+ const targetDef = ctx.workspace.getLinkDefinition(targetId);
6
+ if (!targetDef) ctx.report({
7
+ message: `Actualize references undefined synthesis '^${targetId}'. Define a synthesis with this link ID first.`,
8
+ file: ctx.file,
9
+ location: entry.location,
10
+ sourceMap: ctx.sourceMap,
11
+ data: { target: targetId }
12
+ });
13
+ else if (targetDef.entry.type !== "synthesis_entry") ctx.report({
14
+ message: `Actualize target '^${targetId}' is not a synthesis definition (found '${targetDef.entry.type}' entry).`,
15
+ file: ctx.file,
16
+ location: entry.location,
17
+ sourceMap: ctx.sourceMap,
18
+ data: {
19
+ target: targetId,
20
+ actualKind: targetDef.entry.type
21
+ }
22
+ });
23
+ } };
24
+ /**
25
+ * Check that actualize-synthesis entries reference a valid synthesis definition
26
+ */
27
+ const actualizeUnresolvedTargetRule = {
28
+ code: "actualize-unresolved-target",
29
+ name: "Actualize Unresolved Target",
30
+ description: "An actualize-synthesis must reference a defined synthesis",
31
+ category,
32
+ defaultSeverity: "error",
33
+ dependencies: {
34
+ scope: "entry",
35
+ links: true
36
+ },
37
+ visitor
38
+ };
39
+
40
+ //#endregion
41
+ export { actualizeUnresolvedTargetRule };
42
+ //# sourceMappingURL=actualize-unresolved-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actualize-unresolved-target.js","names":["category: RuleCategory","visitor: RuleVisitor","actualizeUnresolvedTargetRule: Rule"],"sources":["../../../src/checker/rules/actualize-unresolved-target.ts"],"sourcesContent":["import type { Rule, RuleCategory } from \"../rules/rules.js\";\nimport type { RuleVisitor } from \"../visitor.js\";\n\nconst category: RuleCategory = \"link\";\n\nconst visitor: RuleVisitor = {\n visitActualizeEntry(entry, ctx) {\n const targetId = entry.header.target.id;\n const targetDef = ctx.workspace.getLinkDefinition(targetId);\n\n if (!targetDef) {\n ctx.report({\n message: `Actualize references undefined synthesis '^${targetId}'. Define a synthesis with this link ID first.`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { target: targetId },\n });\n } else if (targetDef.entry.type !== \"synthesis_entry\") {\n ctx.report({\n message: `Actualize target '^${targetId}' is not a synthesis definition (found '${targetDef.entry.type}' entry).`,\n file: ctx.file,\n location: entry.location,\n sourceMap: ctx.sourceMap,\n data: { target: targetId, actualKind: targetDef.entry.type },\n });\n }\n },\n};\n\n/**\n * Check that actualize-synthesis entries reference a valid synthesis definition\n */\nexport const actualizeUnresolvedTargetRule: Rule = {\n code: \"actualize-unresolved-target\",\n name: \"Actualize Unresolved Target\",\n description: \"An actualize-synthesis must reference a defined synthesis\",\n category,\n defaultSeverity: \"error\",\n dependencies: { scope: \"entry\", links: true },\n visitor,\n};\n"],"mappings":";AAGA,MAAMA,WAAyB;AAE/B,MAAMC,UAAuB,EAC3B,oBAAoB,OAAO,KAAK;CAC9B,MAAM,WAAW,MAAM,OAAO,OAAO;CACrC,MAAM,YAAY,IAAI,UAAU,kBAAkB,SAAS;AAE3D,KAAI,CAAC,UACH,KAAI,OAAO;EACT,SAAS,8CAA8C,SAAS;EAChE,MAAM,IAAI;EACV,UAAU,MAAM;EAChB,WAAW,IAAI;EACf,MAAM,EAAE,QAAQ,UAAU;EAC3B,CAAC;UACO,UAAU,MAAM,SAAS,kBAClC,KAAI,OAAO;EACT,SAAS,sBAAsB,SAAS,0CAA0C,UAAU,MAAM,KAAK;EACvG,MAAM,IAAI;EACV,UAAU,MAAM;EAChB,WAAW,IAAI;EACf,MAAM;GAAE,QAAQ;GAAU,YAAY,UAAU,MAAM;GAAM;EAC7D,CAAC;GAGP;;;;AAKD,MAAaC,gCAAsC;CACjD,MAAM;CACN,MAAM;CACN,aAAa;CACb;CACA,iBAAiB;CACjB,cAAc;EAAE,OAAO;EAAS,OAAO;EAAM;CAC7C;CACD"}