@domainlang/language 0.1.20

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 (212) hide show
  1. package/README.md +163 -0
  2. package/out/ast-augmentation.d.ts +6 -0
  3. package/out/ast-augmentation.js +2 -0
  4. package/out/ast-augmentation.js.map +1 -0
  5. package/out/domain-lang-module.d.ts +57 -0
  6. package/out/domain-lang-module.js +67 -0
  7. package/out/domain-lang-module.js.map +1 -0
  8. package/out/generated/ast.d.ts +759 -0
  9. package/out/generated/ast.js +556 -0
  10. package/out/generated/ast.js.map +1 -0
  11. package/out/generated/grammar.d.ts +6 -0
  12. package/out/generated/grammar.js +2407 -0
  13. package/out/generated/grammar.js.map +1 -0
  14. package/out/generated/module.d.ts +13 -0
  15. package/out/generated/module.js +21 -0
  16. package/out/generated/module.js.map +1 -0
  17. package/out/index.d.ts +16 -0
  18. package/out/index.js +22 -0
  19. package/out/index.js.map +1 -0
  20. package/out/lsp/domain-lang-code-actions.d.ts +55 -0
  21. package/out/lsp/domain-lang-code-actions.js +143 -0
  22. package/out/lsp/domain-lang-code-actions.js.map +1 -0
  23. package/out/lsp/domain-lang-completion.d.ts +37 -0
  24. package/out/lsp/domain-lang-completion.js +452 -0
  25. package/out/lsp/domain-lang-completion.js.map +1 -0
  26. package/out/lsp/domain-lang-formatter.d.ts +15 -0
  27. package/out/lsp/domain-lang-formatter.js +43 -0
  28. package/out/lsp/domain-lang-formatter.js.map +1 -0
  29. package/out/lsp/domain-lang-naming.d.ts +34 -0
  30. package/out/lsp/domain-lang-naming.js +49 -0
  31. package/out/lsp/domain-lang-naming.js.map +1 -0
  32. package/out/lsp/domain-lang-scope.d.ts +59 -0
  33. package/out/lsp/domain-lang-scope.js +102 -0
  34. package/out/lsp/domain-lang-scope.js.map +1 -0
  35. package/out/lsp/domain-lang-workspace-manager.d.ts +21 -0
  36. package/out/lsp/domain-lang-workspace-manager.js +93 -0
  37. package/out/lsp/domain-lang-workspace-manager.js.map +1 -0
  38. package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
  39. package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
  40. package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
  41. package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
  42. package/out/lsp/hover/domain-lang-hover.js +302 -0
  43. package/out/lsp/hover/domain-lang-hover.js.map +1 -0
  44. package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
  45. package/out/lsp/hover/domain-lang-keywords.js +47 -0
  46. package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
  47. package/out/lsp/manifest-diagnostics.d.ts +82 -0
  48. package/out/lsp/manifest-diagnostics.js +230 -0
  49. package/out/lsp/manifest-diagnostics.js.map +1 -0
  50. package/out/main-browser.d.ts +1 -0
  51. package/out/main-browser.js +11 -0
  52. package/out/main-browser.js.map +1 -0
  53. package/out/main.d.ts +1 -0
  54. package/out/main.js +74 -0
  55. package/out/main.js.map +1 -0
  56. package/out/sdk/ast-augmentation.d.ts +136 -0
  57. package/out/sdk/ast-augmentation.js +62 -0
  58. package/out/sdk/ast-augmentation.js.map +1 -0
  59. package/out/sdk/index.d.ts +94 -0
  60. package/out/sdk/index.js +97 -0
  61. package/out/sdk/index.js.map +1 -0
  62. package/out/sdk/indexes.d.ts +16 -0
  63. package/out/sdk/indexes.js +97 -0
  64. package/out/sdk/indexes.js.map +1 -0
  65. package/out/sdk/loader-node.d.ts +51 -0
  66. package/out/sdk/loader-node.js +119 -0
  67. package/out/sdk/loader-node.js.map +1 -0
  68. package/out/sdk/loader.d.ts +49 -0
  69. package/out/sdk/loader.js +85 -0
  70. package/out/sdk/loader.js.map +1 -0
  71. package/out/sdk/patterns.d.ts +93 -0
  72. package/out/sdk/patterns.js +123 -0
  73. package/out/sdk/patterns.js.map +1 -0
  74. package/out/sdk/query.d.ts +90 -0
  75. package/out/sdk/query.js +679 -0
  76. package/out/sdk/query.js.map +1 -0
  77. package/out/sdk/resolution.d.ts +52 -0
  78. package/out/sdk/resolution.js +68 -0
  79. package/out/sdk/resolution.js.map +1 -0
  80. package/out/sdk/types.d.ts +280 -0
  81. package/out/sdk/types.js +8 -0
  82. package/out/sdk/types.js.map +1 -0
  83. package/out/services/dependency-analyzer.d.ts +58 -0
  84. package/out/services/dependency-analyzer.js +254 -0
  85. package/out/services/dependency-analyzer.js.map +1 -0
  86. package/out/services/dependency-resolver.d.ts +146 -0
  87. package/out/services/dependency-resolver.js +452 -0
  88. package/out/services/dependency-resolver.js.map +1 -0
  89. package/out/services/git-url-resolver.browser.d.ts +10 -0
  90. package/out/services/git-url-resolver.browser.js +19 -0
  91. package/out/services/git-url-resolver.browser.js.map +1 -0
  92. package/out/services/git-url-resolver.d.ts +158 -0
  93. package/out/services/git-url-resolver.js +416 -0
  94. package/out/services/git-url-resolver.js.map +1 -0
  95. package/out/services/governance-validator.d.ts +44 -0
  96. package/out/services/governance-validator.js +153 -0
  97. package/out/services/governance-validator.js.map +1 -0
  98. package/out/services/import-resolver.d.ts +77 -0
  99. package/out/services/import-resolver.js +240 -0
  100. package/out/services/import-resolver.js.map +1 -0
  101. package/out/services/performance-optimizer.d.ts +60 -0
  102. package/out/services/performance-optimizer.js +140 -0
  103. package/out/services/performance-optimizer.js.map +1 -0
  104. package/out/services/relationship-inference.d.ts +11 -0
  105. package/out/services/relationship-inference.js +98 -0
  106. package/out/services/relationship-inference.js.map +1 -0
  107. package/out/services/semver.d.ts +98 -0
  108. package/out/services/semver.js +195 -0
  109. package/out/services/semver.js.map +1 -0
  110. package/out/services/types.d.ts +340 -0
  111. package/out/services/types.js +46 -0
  112. package/out/services/types.js.map +1 -0
  113. package/out/services/workspace-manager.d.ts +123 -0
  114. package/out/services/workspace-manager.js +489 -0
  115. package/out/services/workspace-manager.js.map +1 -0
  116. package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
  117. package/out/syntaxes/domain-lang.monarch.js +29 -0
  118. package/out/syntaxes/domain-lang.monarch.js.map +1 -0
  119. package/out/utils/import-utils.d.ts +49 -0
  120. package/out/utils/import-utils.js +128 -0
  121. package/out/utils/import-utils.js.map +1 -0
  122. package/out/validation/bounded-context.d.ts +11 -0
  123. package/out/validation/bounded-context.js +79 -0
  124. package/out/validation/bounded-context.js.map +1 -0
  125. package/out/validation/classification.d.ts +3 -0
  126. package/out/validation/classification.js +3 -0
  127. package/out/validation/classification.js.map +1 -0
  128. package/out/validation/constants.d.ts +180 -0
  129. package/out/validation/constants.js +235 -0
  130. package/out/validation/constants.js.map +1 -0
  131. package/out/validation/domain-lang-validator.d.ts +2 -0
  132. package/out/validation/domain-lang-validator.js +27 -0
  133. package/out/validation/domain-lang-validator.js.map +1 -0
  134. package/out/validation/domain.d.ts +11 -0
  135. package/out/validation/domain.js +63 -0
  136. package/out/validation/domain.js.map +1 -0
  137. package/out/validation/import.d.ts +68 -0
  138. package/out/validation/import.js +237 -0
  139. package/out/validation/import.js.map +1 -0
  140. package/out/validation/manifest.d.ts +144 -0
  141. package/out/validation/manifest.js +327 -0
  142. package/out/validation/manifest.js.map +1 -0
  143. package/out/validation/maps.d.ts +21 -0
  144. package/out/validation/maps.js +60 -0
  145. package/out/validation/maps.js.map +1 -0
  146. package/out/validation/metadata.d.ts +7 -0
  147. package/out/validation/metadata.js +16 -0
  148. package/out/validation/metadata.js.map +1 -0
  149. package/out/validation/model.d.ts +12 -0
  150. package/out/validation/model.js +29 -0
  151. package/out/validation/model.js.map +1 -0
  152. package/out/validation/relationships.d.ts +12 -0
  153. package/out/validation/relationships.js +94 -0
  154. package/out/validation/relationships.js.map +1 -0
  155. package/out/validation/shared.d.ts +6 -0
  156. package/out/validation/shared.js +12 -0
  157. package/out/validation/shared.js.map +1 -0
  158. package/package.json +110 -0
  159. package/src/ast-augmentation.ts +5 -0
  160. package/src/domain-lang-module.ts +112 -0
  161. package/src/domain-lang.langium +351 -0
  162. package/src/generated/ast.ts +986 -0
  163. package/src/generated/grammar.ts +2409 -0
  164. package/src/generated/module.ts +25 -0
  165. package/src/index.ts +24 -0
  166. package/src/lsp/domain-lang-code-actions.ts +189 -0
  167. package/src/lsp/domain-lang-completion.ts +514 -0
  168. package/src/lsp/domain-lang-formatter.ts +51 -0
  169. package/src/lsp/domain-lang-naming.ts +56 -0
  170. package/src/lsp/domain-lang-scope.ts +137 -0
  171. package/src/lsp/domain-lang-workspace-manager.ts +104 -0
  172. package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
  173. package/src/lsp/hover/domain-lang-hover.ts +338 -0
  174. package/src/lsp/hover/domain-lang-keywords.ts +50 -0
  175. package/src/lsp/manifest-diagnostics.ts +290 -0
  176. package/src/main-browser.ts +15 -0
  177. package/src/main.ts +85 -0
  178. package/src/sdk/README.md +297 -0
  179. package/src/sdk/ast-augmentation.ts +157 -0
  180. package/src/sdk/index.ts +126 -0
  181. package/src/sdk/indexes.ts +155 -0
  182. package/src/sdk/loader-node.ts +146 -0
  183. package/src/sdk/loader.ts +99 -0
  184. package/src/sdk/patterns.ts +147 -0
  185. package/src/sdk/query.ts +802 -0
  186. package/src/sdk/resolution.ts +78 -0
  187. package/src/sdk/types.ts +323 -0
  188. package/src/services/dependency-analyzer.ts +321 -0
  189. package/src/services/dependency-resolver.ts +551 -0
  190. package/src/services/git-url-resolver.browser.ts +26 -0
  191. package/src/services/git-url-resolver.ts +517 -0
  192. package/src/services/governance-validator.ts +177 -0
  193. package/src/services/import-resolver.ts +292 -0
  194. package/src/services/performance-optimizer.ts +170 -0
  195. package/src/services/relationship-inference.ts +121 -0
  196. package/src/services/semver.ts +213 -0
  197. package/src/services/types.ts +415 -0
  198. package/src/services/workspace-manager.ts +607 -0
  199. package/src/syntaxes/domain-lang.monarch.ts +29 -0
  200. package/src/utils/import-utils.ts +152 -0
  201. package/src/validation/bounded-context.ts +99 -0
  202. package/src/validation/classification.ts +5 -0
  203. package/src/validation/constants.ts +304 -0
  204. package/src/validation/domain-lang-validator.ts +33 -0
  205. package/src/validation/domain.ts +77 -0
  206. package/src/validation/import.ts +295 -0
  207. package/src/validation/manifest.ts +439 -0
  208. package/src/validation/maps.ts +76 -0
  209. package/src/validation/metadata.ts +18 -0
  210. package/src/validation/model.ts +37 -0
  211. package/src/validation/relationships.ts +154 -0
  212. package/src/validation/shared.ts +14 -0
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Validation message constants for DomainLang.
3
+ *
4
+ * Centralizes all validation messages to ensure consistency
5
+ * and facilitate internationalization in the future.
6
+ *
7
+ * Messages follow VS Code conventions:
8
+ * - Clear problem statement
9
+ * - Brief DDD context explaining why it matters
10
+ * - Inline example showing the fix
11
+ * - Clickable documentation link via CodeDescription
12
+ */
13
+ // ============================================================================
14
+ // Issue Codes for Code Actions
15
+ // ============================================================================
16
+ /**
17
+ * Diagnostic codes used to identify validation issues.
18
+ * Code actions match on these codes to provide quick fixes.
19
+ *
20
+ * Naming convention: CATEGORY_SPECIFIC_ISSUE
21
+ */
22
+ export const IssueCodes = {
23
+ // Import & Dependency Issues
24
+ ImportMissingUri: 'import-missing-uri',
25
+ ImportRequiresManifest: 'import-requires-manifest',
26
+ ImportNotInManifest: 'import-not-in-manifest',
27
+ ImportNotInstalled: 'import-not-installed',
28
+ ImportConflictingSourcePath: 'import-conflicting-source-path',
29
+ ImportMissingSourceOrPath: 'import-missing-source-or-path',
30
+ ImportMissingRef: 'import-missing-ref',
31
+ ImportAbsolutePath: 'import-absolute-path',
32
+ ImportEscapesWorkspace: 'import-escapes-workspace',
33
+ // Domain Issues
34
+ DomainNoVision: 'domain-no-vision',
35
+ DomainCircularHierarchy: 'domain-circular-hierarchy',
36
+ // Bounded Context Issues
37
+ BoundedContextNoDescription: 'bounded-context-no-description',
38
+ BoundedContextNoDomain: 'bounded-context-no-domain',
39
+ BoundedContextClassificationConflict: 'bounded-context-classification-conflict',
40
+ BoundedContextTeamConflict: 'bounded-context-team-conflict',
41
+ // Integration Pattern Issues
42
+ SharedKernelNotBidirectional: 'shared-kernel-not-bidirectional',
43
+ AclOnWrongSide: 'acl-on-wrong-side',
44
+ ConformistOnWrongSide: 'conformist-on-wrong-side',
45
+ TooManyPatterns: 'too-many-patterns',
46
+ // Context/Domain Map Issues
47
+ ContextMapNoContexts: 'context-map-no-contexts',
48
+ ContextMapNoRelationships: 'context-map-no-relationships',
49
+ DomainMapNoDomains: 'domain-map-no-domains',
50
+ // Metadata Issues
51
+ MetadataMissingName: 'metadata-missing-name',
52
+ // General Issues
53
+ DuplicateElement: 'duplicate-element'
54
+ };
55
+ // ============================================================================
56
+ // Documentation Link Utilities
57
+ // ============================================================================
58
+ const REPO_BASE = 'https://github.com/larsbaunwall/DomainLang/blob/main';
59
+ const DOCS_BASE = `${REPO_BASE}/dsl/domain-lang/docs`;
60
+ /**
61
+ * Builds a documentation URL for error messages.
62
+ * @param docPath - Relative path from docs/ folder
63
+ * @param anchor - Optional section anchor (without #)
64
+ */
65
+ const buildDocLink = (docPath, anchor) => `${DOCS_BASE}/${docPath}${anchor ? `#${anchor}` : ''}`;
66
+ /**
67
+ * Creates a CodeDescription for clickable documentation links in VS Code.
68
+ * @param docPath - Relative path from docs/ folder
69
+ * @param anchor - Optional section anchor (without #)
70
+ */
71
+ export const buildCodeDescription = (docPath, anchor) => ({
72
+ href: buildDocLink(docPath, anchor)
73
+ });
74
+ // ============================================================================
75
+ // Enhanced Validation Messages
76
+ // ============================================================================
77
+ export const ValidationMessages = {
78
+ /**
79
+ * Warning message when a domain lacks a vision statement.
80
+ * @param name - The name of the domain
81
+ */
82
+ DOMAIN_NO_VISION: (name) => `Domain '${name}' is missing a vision statement.`,
83
+ /**
84
+ * Error message when a circular domain hierarchy is detected.
85
+ * @param cycle - Array of domain names forming the cycle
86
+ */
87
+ DOMAIN_CIRCULAR_HIERARCHY: (cycle) => `Circular domain hierarchy detected: ${cycle.join(' → ')}.`,
88
+ /**
89
+ * Warning message when a bounded context lacks a description.
90
+ * @param name - The name of the bounded context
91
+ */
92
+ BOUNDED_CONTEXT_NO_DESCRIPTION: (name) => `Bounded Context '${name}' is missing a description.`,
93
+ /**
94
+ * Warning message when a bounded context lacks a domain reference.
95
+ * @param name - The name of the bounded context
96
+ */
97
+ BOUNDED_CONTEXT_NO_DOMAIN: (name) => `Bounded Context '${name}' must belong to a domain. Use 'for DomainName'.`,
98
+ /**
99
+ * Warning when classification is specified both inline and in a block.
100
+ * Inline value takes precedence.
101
+ * @param bcName - The name of the bounded context
102
+ * @param inlineClassification - The inline classification name (from 'as')
103
+ * @param blockClassification - The block classification name (from 'classification:')
104
+ */
105
+ BOUNDED_CONTEXT_CLASSIFICATION_CONFLICT: (bcName, inlineClassification, blockClassification) => `Classification specified both inline${inlineClassification ? ` ('as ${inlineClassification}')` : ''} and in block${blockClassification ? ` ('classification: ${blockClassification}')` : ''}. Inline value takes precedence.`,
106
+ /**
107
+ * Warning when team is specified both inline and in a block.
108
+ * Inline value takes precedence.
109
+ * @param bcName - The name of the bounded context
110
+ * @param inlineTeam - The inline team name (from 'by')
111
+ * @param blockTeam - The block team name (from 'team:')
112
+ */
113
+ BOUNDED_CONTEXT_TEAM_CONFLICT: (bcName, inlineTeam, blockTeam) => `Team specified both inline${inlineTeam ? ` ('by ${inlineTeam}')` : ''} and in block${blockTeam ? ` ('team: ${blockTeam}')` : ''}. Inline value takes precedence.`,
114
+ /**
115
+ * Error message when an element is defined multiple times.
116
+ * @param fqn - The fully qualified name of the duplicate element
117
+ */
118
+ DUPLICATE_ELEMENT: (fqn) => `Duplicate element: '${fqn}' is already defined.`,
119
+ // ========================================================================
120
+ // Integration Pattern & Relationship Validation
121
+ // ========================================================================
122
+ /**
123
+ * Warning when SharedKernel pattern uses incorrect arrow direction.
124
+ * SharedKernel requires bidirectional relationship.
125
+ */
126
+ SHARED_KERNEL_MUST_BE_BIDIRECTIONAL: (leftContext, rightContext, arrow) => `SharedKernel between '${leftContext}' and '${rightContext}' requires bidirectional arrow '<->', not '${arrow}'.`,
127
+ /**
128
+ * Warning when Anti-Corruption Layer is on the wrong side of relationship.
129
+ * ACL should protect the consuming context (downstream).
130
+ */
131
+ ACL_ON_WRONG_SIDE: (context, side) => `Anti-Corruption Layer (ACL) on '${context}' should be on downstream (consuming) side, not ${side} side.`,
132
+ /**
133
+ * Warning when Conformist pattern is on the wrong side.
134
+ * Conformist accepts upstream model without translation.
135
+ */
136
+ CONFORMIST_ON_WRONG_SIDE: (context, side) => `Conformist (CF) on '${context}' should be on downstream (consuming) side, not ${side} side.`,
137
+ /**
138
+ * Info message when relationship has too many integration patterns.
139
+ * Suggests possible syntax confusion.
140
+ */
141
+ TOO_MANY_PATTERNS: (count, side) => `Too many integration patterns (${count}) on ${side} side. Typically use 1-2 patterns per side.`,
142
+ // ========================================================================
143
+ // Import & Dependency Validation (PRS-010 Phase 8)
144
+ // ========================================================================
145
+ /**
146
+ * Error when import statement has no URI.
147
+ */
148
+ IMPORT_MISSING_URI: () => `Import statement must have a URI. Use: import "package" or import "./local-path"`,
149
+ /**
150
+ * Error when external dependency requires model.yaml but none exists.
151
+ * @param specifier - The import specifier (e.g., "core", "domainlang/patterns")
152
+ */
153
+ IMPORT_REQUIRES_MANIFEST: (specifier) => `External dependency '${specifier}' requires model.yaml.\n` +
154
+ `Hint: Create model.yaml and add the dependency:\n` +
155
+ ` dependencies:\n` +
156
+ ` ${specifier}:\n` +
157
+ ` ref: v1.0.0`,
158
+ /**
159
+ * Error when import specifier not found in manifest dependencies.
160
+ * @param alias - The dependency alias/specifier
161
+ */
162
+ IMPORT_NOT_IN_MANIFEST: (alias) => `Import '${alias}' not found in model.yaml dependencies.\n` +
163
+ `Hint: Run 'dlang add ${alias} <source>@<ref>' to add it, or manually add to model.yaml:\n` +
164
+ ` dependencies:\n` +
165
+ ` ${alias}:\n` +
166
+ ` ref: v1.0.0`,
167
+ /**
168
+ * Error when dependency not installed (no lock file entry).
169
+ * @param alias - The dependency alias
170
+ */
171
+ IMPORT_NOT_INSTALLED: (alias) => `Dependency '${alias}' not installed.\n` +
172
+ `Hint: Run 'dlang install' to fetch dependencies and generate model.lock.`,
173
+ /**
174
+ * Error when dependency has conflicting source and path definitions.
175
+ * @param alias - The dependency alias
176
+ */
177
+ IMPORT_CONFLICTING_SOURCE_PATH: (alias) => `Dependency '${alias}' cannot define both 'source' and 'path' in model.yaml.\n` +
178
+ `Hint: Use 'source' for git-based packages or 'path' for local workspace packages.`,
179
+ /**
180
+ * Error when dependency is missing both source and path.
181
+ * @param alias - The dependency alias
182
+ */
183
+ IMPORT_MISSING_SOURCE_OR_PATH: (alias) => `Dependency '${alias}' must define either 'source' or 'path' in model.yaml.\n` +
184
+ `Hint: Add 'source: owner/repo' for git packages, or 'path: ./local/path' for local packages.`,
185
+ /**
186
+ * Error when git dependency is missing ref (tag, branch, or commit).
187
+ * @param alias - The dependency alias
188
+ */
189
+ IMPORT_MISSING_REF: (alias) => `Dependency '${alias}' must specify a git ref in model.yaml.\n` +
190
+ `Hint: Add a git ref: 'ref: v1.0.0' (tag), 'ref: main' (branch), or a commit SHA.`,
191
+ /**
192
+ * Error when local path uses absolute path.
193
+ * @param alias - The dependency alias
194
+ * @param absolutePath - The absolute path that was specified
195
+ */
196
+ IMPORT_ABSOLUTE_PATH: (alias, absolutePath) => `Local path dependency '${alias}' cannot use absolute path '${absolutePath}'.\n` +
197
+ `Hint: Use a relative path from the workspace root, e.g., 'path: ./packages/shared'.`,
198
+ /**
199
+ * Error when local path escapes workspace boundary.
200
+ * @param alias - The dependency alias
201
+ */
202
+ IMPORT_ESCAPES_WORKSPACE: (alias) => `Local path dependency '${alias}' escapes workspace boundary.\n` +
203
+ `Hint: Local dependencies must be within the workspace. Consider moving the dependency or using a git-based source.`,
204
+ // ========================================================================
205
+ // Context Map & Domain Map Validation
206
+ // ========================================================================
207
+ /**
208
+ * Warning when context map contains no bounded contexts.
209
+ * @param name - The context map name
210
+ */
211
+ CONTEXT_MAP_NO_CONTEXTS: (name) => `Context Map '${name}' contains no bounded contexts.\n` +
212
+ `Hint: Use 'contains ContextA, ContextB' to specify which contexts are in the map.`,
213
+ /**
214
+ * Info when context map has multiple contexts but no relationships.
215
+ * @param name - The context map name
216
+ * @param count - Number of contexts
217
+ */
218
+ CONTEXT_MAP_NO_RELATIONSHIPS: (name, count) => `Context Map '${name}' contains ${count} contexts but no documented relationships.\n` +
219
+ `Hint: Add relationships to show how contexts integrate (e.g., '[OHS] A -> [CF] B').`,
220
+ /**
221
+ * Warning when domain map contains no domains.
222
+ * @param name - The domain map name
223
+ */
224
+ DOMAIN_MAP_NO_DOMAINS: (name) => `Domain Map '${name}' contains no domains.\n` +
225
+ `Hint: Use 'contains DomainA, DomainB' to specify which domains are in the map.`,
226
+ // ========================================================================
227
+ // Metadata Validation
228
+ // ========================================================================
229
+ /**
230
+ * Error when metadata is missing a name.
231
+ */
232
+ METADATA_MISSING_NAME: () => `Metadata must have a name.\n` +
233
+ `Hint: Define metadata with: Metadata MyMetadata { ... }`
234
+ };
235
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/validation/constants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { DomainLangServices } from '../domain-lang-module.js';
2
+ export declare function registerValidationChecks(services: DomainLangServices): void;
@@ -0,0 +1,27 @@
1
+ import { modelChecks } from './model.js';
2
+ import { domainChecks } from './domain.js';
3
+ import { boundedContextChecks } from './bounded-context.js';
4
+ import { classificationChecks } from './classification.js';
5
+ import { metadataChecks } from './metadata.js';
6
+ import { contextMapChecks, domainMapChecks } from './maps.js';
7
+ import { relationshipChecks } from './relationships.js';
8
+ import { createImportChecks } from './import.js';
9
+ export function registerValidationChecks(services) {
10
+ const registry = services.validation.ValidationRegistry;
11
+ // Get import checks
12
+ const importChecks = createImportChecks(services);
13
+ // Compose the pipeline for each type
14
+ const pipeline = {
15
+ Model: modelChecks,
16
+ Domain: domainChecks,
17
+ BoundedContext: boundedContextChecks,
18
+ Classification: classificationChecks,
19
+ ContextMap: contextMapChecks,
20
+ DomainMap: domainMapChecks,
21
+ Metadata: metadataChecks.Metadata,
22
+ ImportStatement: importChecks.ImportStatement,
23
+ Relationship: relationshipChecks,
24
+ };
25
+ registry.register(pipeline);
26
+ }
27
+ //# sourceMappingURL=domain-lang-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-lang-validator.js","sourceRoot":"","sources":["../../src/validation/domain-lang-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKjD,MAAM,UAAU,wBAAwB,CAAC,QAA4B;IACjE,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,kBAAkB,CAAC;IAExD,oBAAoB;IACpB,MAAM,YAAY,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAElD,qCAAqC;IACrC,MAAM,QAAQ,GAAwC;QAClD,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,YAAY;QACpB,cAAc,EAAE,oBAAoB;QACpC,cAAc,EAAE,oBAAoB;QACpC,UAAU,EAAE,gBAAgB;QAC5B,SAAS,EAAE,eAAe;QAC1B,QAAQ,EAAE,cAAc,CAAC,QAAQ;QACjC,eAAe,EAAE,YAAY,CAAC,eAAe;QAC7C,YAAY,EAAE,kBAAkB;KACnC,CAAC;IAEF,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ValidationAcceptor } from 'langium';
2
+ import type { Domain } from '../generated/ast.js';
3
+ /**
4
+ * Validates that a domain has a vision statement.
5
+ *
6
+ * @param domain - The domain to validate
7
+ * @param accept - The validation acceptor for reporting issues
8
+ */
9
+ declare function validateDomainHasVision(domain: Domain, accept: ValidationAcceptor): void;
10
+ export declare const domainChecks: (typeof validateDomainHasVision)[];
11
+ export {};
@@ -0,0 +1,63 @@
1
+ import { ValidationMessages, buildCodeDescription } from './constants.js';
2
+ /**
3
+ * Validates that a domain has a vision statement.
4
+ *
5
+ * @param domain - The domain to validate
6
+ * @param accept - The validation acceptor for reporting issues
7
+ */
8
+ function validateDomainHasVision(domain, accept) {
9
+ if (!domain.vision) {
10
+ accept('warning', ValidationMessages.DOMAIN_NO_VISION(domain.name), {
11
+ node: domain,
12
+ keyword: 'Domain',
13
+ codeDescription: buildCodeDescription('language.md', 'domain-vision')
14
+ });
15
+ }
16
+ }
17
+ /**
18
+ * Validates that a domain hierarchy does not contain circular references.
19
+ *
20
+ * The `Domain A in B` syntax expresses subdomain containment.
21
+ * Circular containment (A in B, B in C, C in A) is semantically invalid
22
+ * because it violates the fundamental concept of domain decomposition.
23
+ *
24
+ * @param domain - The domain to validate
25
+ * @param accept - The validation acceptor for reporting issues
26
+ */
27
+ function validateNoCyclicDomainHierarchy(domain, accept) {
28
+ // Only check if this domain has a parent
29
+ if (!domain.parent?.ref) {
30
+ return;
31
+ }
32
+ const visited = new Set();
33
+ const path = [domain.name];
34
+ let current = domain.parent.ref;
35
+ while (current) {
36
+ // Check if we've encountered this domain before (cycle detected)
37
+ if (visited.has(current)) {
38
+ // We found a cycle - report it
39
+ path.push(current.name);
40
+ accept('error', ValidationMessages.DOMAIN_CIRCULAR_HIERARCHY(path), {
41
+ node: domain,
42
+ property: 'parent',
43
+ codeDescription: buildCodeDescription('language.md', 'domain-hierarchy')
44
+ });
45
+ return;
46
+ }
47
+ // Check if we've looped back to the starting domain
48
+ if (current === domain) {
49
+ path.push(domain.name);
50
+ accept('error', ValidationMessages.DOMAIN_CIRCULAR_HIERARCHY(path), {
51
+ node: domain,
52
+ property: 'parent',
53
+ codeDescription: buildCodeDescription('language.md', 'domain-hierarchy')
54
+ });
55
+ return;
56
+ }
57
+ visited.add(current);
58
+ path.push(current.name);
59
+ current = current.parent?.ref;
60
+ }
61
+ }
62
+ export const domainChecks = [validateDomainHasVision, validateNoCyclicDomainHierarchy];
63
+ //# sourceMappingURL=domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain.js","sourceRoot":"","sources":["../../src/validation/domain.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;;;GAKG;AACH,SAAS,uBAAuB,CAC5B,MAAc,EACd,MAA0B;IAE1B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;YAChE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,QAAQ;YACjB,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,eAAe,CAAC;SACxE,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,+BAA+B,CACpC,MAAc,EACd,MAA0B;IAE1B,yCAAyC;IACzC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;QACtB,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,IAAI,GAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO,GAAuB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAEpD,OAAO,OAAO,EAAE,CAAC;QACb,iEAAiE;QACjE,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,+BAA+B;YAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxB,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE;gBAChE,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;aAC3E,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,oDAAoD;QACpD,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACvB,MAAM,CAAC,OAAO,EAAE,kBAAkB,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE;gBAChE,IAAI,EAAE,MAAM;gBACZ,QAAQ,EAAE,QAAQ;gBAClB,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;aAC3E,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;IAClC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,uBAAuB,EAAE,+BAA+B,CAAC,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { ValidationAcceptor, ValidationChecks } from 'langium';
2
+ import { Cancellation } from 'langium';
3
+ import type { DomainLangAstType, ImportStatement } from '../generated/ast.js';
4
+ import type { DomainLangServices } from '../domain-lang-module.js';
5
+ import type { LangiumDocument } from 'langium';
6
+ /**
7
+ * Validates import statements in DomainLang.
8
+ *
9
+ * Uses async validators (Langium 4.x supports MaybePromise<void>) to leverage
10
+ * the shared WorkspaceManager service with its cached manifest/lock file reading.
11
+ *
12
+ * Checks:
13
+ * - External imports require manifest + alias
14
+ * - Local path dependencies stay inside workspace
15
+ * - Lock file exists for external dependencies
16
+ */
17
+ export declare class ImportValidator {
18
+ private readonly workspaceManager;
19
+ constructor(services: DomainLangServices);
20
+ /**
21
+ * Validates an import statement asynchronously.
22
+ *
23
+ * Langium validators can return MaybePromise<void>, enabling async operations
24
+ * like reading manifests via the shared, cached WorkspaceManager.
25
+ */
26
+ checkImportPath(imp: ImportStatement, accept: ValidationAcceptor, document: LangiumDocument, _cancelToken: Cancellation.CancellationToken): Promise<void>;
27
+ /**
28
+ * Determines if an import URI is external (requires manifest).
29
+ *
30
+ * Per PRS-010:
31
+ * - Local relative: ./path, ../path
32
+ * - Path aliases: @/path, @alias/path (resolved via manifest paths section)
33
+ * - External: owner/package (requires manifest dependencies)
34
+ */
35
+ private isExternalImport;
36
+ /**
37
+ * Gets the normalized dependency configuration for an alias.
38
+ */
39
+ private getDependency;
40
+ /**
41
+ * Validates dependency configuration.
42
+ */
43
+ private validateDependencyConfig;
44
+ /**
45
+ * Validates local path dependencies stay within workspace.
46
+ */
47
+ private validateLocalPathDependency;
48
+ /**
49
+ * Validates that external dependency is in lock file and cached.
50
+ */
51
+ private validateCachedPackage;
52
+ /**
53
+ * Gets the cache directory for a dependency.
54
+ * Per PRS-010: Project-local cache at .dlang/packages/{owner}/{repo}/{commit}/
55
+ */
56
+ private getCacheDirectory;
57
+ /**
58
+ * Checks if a directory exists (async).
59
+ */
60
+ private directoryExists;
61
+ }
62
+ /**
63
+ * Creates validation checks for import statements.
64
+ *
65
+ * Returns async validators that leverage the shared WorkspaceManager
66
+ * for cached manifest/lock file reading.
67
+ */
68
+ export declare function createImportChecks(services: DomainLangServices): ValidationChecks<DomainLangAstType>;
@@ -0,0 +1,237 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { ValidationMessages, buildCodeDescription, IssueCodes } from './constants.js';
4
+ /**
5
+ * Validates import statements in DomainLang.
6
+ *
7
+ * Uses async validators (Langium 4.x supports MaybePromise<void>) to leverage
8
+ * the shared WorkspaceManager service with its cached manifest/lock file reading.
9
+ *
10
+ * Checks:
11
+ * - External imports require manifest + alias
12
+ * - Local path dependencies stay inside workspace
13
+ * - Lock file exists for external dependencies
14
+ */
15
+ export class ImportValidator {
16
+ constructor(services) {
17
+ this.workspaceManager = services.imports.WorkspaceManager;
18
+ }
19
+ /**
20
+ * Validates an import statement asynchronously.
21
+ *
22
+ * Langium validators can return MaybePromise<void>, enabling async operations
23
+ * like reading manifests via the shared, cached WorkspaceManager.
24
+ */
25
+ async checkImportPath(imp, accept, document, _cancelToken) {
26
+ if (!imp.uri) {
27
+ accept('error', ValidationMessages.IMPORT_MISSING_URI(), {
28
+ node: imp,
29
+ keyword: 'import',
30
+ codeDescription: buildCodeDescription('language.md', 'imports'),
31
+ data: { code: IssueCodes.ImportMissingUri }
32
+ });
33
+ return;
34
+ }
35
+ if (!this.isExternalImport(imp.uri)) {
36
+ return;
37
+ }
38
+ // Initialize workspace manager from document location
39
+ const docDir = path.dirname(document.uri.fsPath);
40
+ await this.workspaceManager.initialize(docDir);
41
+ const manifest = await this.workspaceManager.getManifest();
42
+ if (!manifest) {
43
+ accept('error', ValidationMessages.IMPORT_REQUIRES_MANIFEST(imp.uri), {
44
+ node: imp,
45
+ property: 'uri',
46
+ codeDescription: buildCodeDescription('language.md', 'imports'),
47
+ data: { code: IssueCodes.ImportRequiresManifest, specifier: imp.uri }
48
+ });
49
+ return;
50
+ }
51
+ const alias = imp.uri.split('/')[0];
52
+ const dependency = this.getDependency(manifest, alias);
53
+ if (!dependency) {
54
+ accept('error', ValidationMessages.IMPORT_NOT_IN_MANIFEST(alias), {
55
+ node: imp,
56
+ property: 'uri',
57
+ codeDescription: buildCodeDescription('language.md', 'imports'),
58
+ data: { code: IssueCodes.ImportNotInManifest, alias }
59
+ });
60
+ return;
61
+ }
62
+ this.validateDependencyConfig(dependency, alias, accept, imp);
63
+ // External source dependencies require lock file and cached packages
64
+ if (dependency.source) {
65
+ const lockFile = await this.workspaceManager.getLockFile();
66
+ if (!lockFile) {
67
+ accept('error', ValidationMessages.IMPORT_NOT_INSTALLED(alias), {
68
+ node: imp,
69
+ property: 'uri',
70
+ codeDescription: buildCodeDescription('language.md', 'imports'),
71
+ data: { code: IssueCodes.ImportNotInstalled, alias }
72
+ });
73
+ return;
74
+ }
75
+ await this.validateCachedPackage(dependency, alias, lockFile, accept, imp);
76
+ }
77
+ }
78
+ /**
79
+ * Determines if an import URI is external (requires manifest).
80
+ *
81
+ * Per PRS-010:
82
+ * - Local relative: ./path, ../path
83
+ * - Path aliases: @/path, @alias/path (resolved via manifest paths section)
84
+ * - External: owner/package (requires manifest dependencies)
85
+ */
86
+ isExternalImport(uri) {
87
+ if (uri.startsWith('./') || uri.startsWith('../')) {
88
+ return false;
89
+ }
90
+ if (uri.startsWith('@')) {
91
+ return false;
92
+ }
93
+ return true;
94
+ }
95
+ /**
96
+ * Gets the normalized dependency configuration for an alias.
97
+ */
98
+ getDependency(manifest, alias) {
99
+ const dep = manifest.dependencies?.[alias];
100
+ if (!dep) {
101
+ return undefined;
102
+ }
103
+ if (typeof dep === 'string') {
104
+ return { source: alias, ref: dep };
105
+ }
106
+ if (!dep.source && !dep.path) {
107
+ return { ...dep, source: alias };
108
+ }
109
+ return dep;
110
+ }
111
+ /**
112
+ * Validates dependency configuration.
113
+ */
114
+ validateDependencyConfig(dependency, alias, accept, imp) {
115
+ if (dependency.source && dependency.path) {
116
+ accept('error', ValidationMessages.IMPORT_CONFLICTING_SOURCE_PATH(alias), {
117
+ node: imp,
118
+ property: 'uri',
119
+ codeDescription: buildCodeDescription('language.md', 'imports'),
120
+ data: { code: IssueCodes.ImportConflictingSourcePath, alias }
121
+ });
122
+ return;
123
+ }
124
+ if (!dependency.source && !dependency.path) {
125
+ accept('error', ValidationMessages.IMPORT_MISSING_SOURCE_OR_PATH(alias), {
126
+ node: imp,
127
+ property: 'uri',
128
+ codeDescription: buildCodeDescription('language.md', 'imports'),
129
+ data: { code: IssueCodes.ImportMissingSourceOrPath, alias }
130
+ });
131
+ return;
132
+ }
133
+ if (dependency.source && !dependency.ref) {
134
+ accept('error', ValidationMessages.IMPORT_MISSING_REF(alias), {
135
+ node: imp,
136
+ property: 'uri',
137
+ codeDescription: buildCodeDescription('language.md', 'imports'),
138
+ data: { code: IssueCodes.ImportMissingRef, alias }
139
+ });
140
+ }
141
+ if (dependency.path) {
142
+ this.validateLocalPathDependency(dependency.path, alias, accept, imp);
143
+ }
144
+ }
145
+ /**
146
+ * Validates local path dependencies stay within workspace.
147
+ */
148
+ validateLocalPathDependency(dependencyPath, alias, accept, imp) {
149
+ if (path.isAbsolute(dependencyPath)) {
150
+ accept('error', ValidationMessages.IMPORT_ABSOLUTE_PATH(alias, dependencyPath), {
151
+ node: imp,
152
+ property: 'uri',
153
+ codeDescription: buildCodeDescription('language.md', 'imports'),
154
+ data: { code: IssueCodes.ImportAbsolutePath, alias, path: dependencyPath }
155
+ });
156
+ return;
157
+ }
158
+ const workspaceRoot = this.workspaceManager.getWorkspaceRoot();
159
+ const resolvedPath = path.resolve(workspaceRoot, dependencyPath);
160
+ const relativeToWorkspace = path.relative(workspaceRoot, resolvedPath);
161
+ if (relativeToWorkspace.startsWith('..') || path.isAbsolute(relativeToWorkspace)) {
162
+ accept('error', ValidationMessages.IMPORT_ESCAPES_WORKSPACE(alias), {
163
+ node: imp,
164
+ property: 'uri',
165
+ codeDescription: buildCodeDescription('language.md', 'imports'),
166
+ data: { code: IssueCodes.ImportEscapesWorkspace, alias }
167
+ });
168
+ }
169
+ }
170
+ /**
171
+ * Validates that external dependency is in lock file and cached.
172
+ */
173
+ async validateCachedPackage(dependency, alias, lockFile, accept, imp) {
174
+ // Source is guaranteed to exist when this method is called (see caller)
175
+ const packageKey = dependency.source ?? alias;
176
+ const lockedDep = lockFile.dependencies[packageKey];
177
+ if (!lockedDep) {
178
+ accept('error', ValidationMessages.IMPORT_NOT_INSTALLED(alias), {
179
+ node: imp,
180
+ property: 'uri',
181
+ codeDescription: buildCodeDescription('language.md', 'imports'),
182
+ data: { code: IssueCodes.ImportNotInstalled, alias }
183
+ });
184
+ return;
185
+ }
186
+ const workspaceRoot = this.workspaceManager.getWorkspaceRoot();
187
+ const cacheDir = this.getCacheDirectory(workspaceRoot, packageKey, lockedDep.commit);
188
+ const cacheExists = await this.directoryExists(cacheDir);
189
+ if (!cacheExists) {
190
+ accept('error', ValidationMessages.IMPORT_NOT_INSTALLED(alias), {
191
+ node: imp,
192
+ property: 'uri',
193
+ codeDescription: buildCodeDescription('language.md', 'imports'),
194
+ data: { code: IssueCodes.ImportNotInstalled, alias }
195
+ });
196
+ }
197
+ }
198
+ /**
199
+ * Gets the cache directory for a dependency.
200
+ * Per PRS-010: Project-local cache at .dlang/packages/{owner}/{repo}/{commit}/
201
+ */
202
+ getCacheDirectory(workspaceRoot, source, commitHash) {
203
+ const [owner, repo] = source.split('/');
204
+ return path.join(workspaceRoot, '.dlang', 'packages', owner, repo, commitHash);
205
+ }
206
+ /**
207
+ * Checks if a directory exists (async).
208
+ */
209
+ async directoryExists(dirPath) {
210
+ try {
211
+ const stat = await fs.stat(dirPath);
212
+ return stat.isDirectory();
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }
218
+ }
219
+ /**
220
+ * Creates validation checks for import statements.
221
+ *
222
+ * Returns async validators that leverage the shared WorkspaceManager
223
+ * for cached manifest/lock file reading.
224
+ */
225
+ export function createImportChecks(services) {
226
+ const validator = new ImportValidator(services);
227
+ return {
228
+ // Langium 4.x supports async validators via MaybePromise<void>
229
+ ImportStatement: async (imp, accept, cancelToken) => {
230
+ const document = imp.$document;
231
+ if (!document)
232
+ return;
233
+ await validator.checkImportPath(imp, accept, document, cancelToken);
234
+ }
235
+ };
236
+ }
237
+ //# sourceMappingURL=import.js.map