@domainlang/language 0.1.81
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.
- package/README.md +32 -0
- package/out/ast-augmentation.d.ts +6 -0
- package/out/ast-augmentation.js +2 -0
- package/out/ast-augmentation.js.map +1 -0
- package/out/domain-lang-module.d.ts +55 -0
- package/out/domain-lang-module.js +59 -0
- package/out/domain-lang-module.js.map +1 -0
- package/out/generated/ast.d.ts +770 -0
- package/out/generated/ast.js +565 -0
- package/out/generated/ast.js.map +1 -0
- package/out/generated/grammar.d.ts +6 -0
- package/out/generated/grammar.js +2502 -0
- package/out/generated/grammar.js.map +1 -0
- package/out/generated/module.d.ts +13 -0
- package/out/generated/module.js +21 -0
- package/out/generated/module.js.map +1 -0
- package/out/index.d.ts +13 -0
- package/out/index.js +17 -0
- package/out/index.js.map +1 -0
- package/out/lsp/domain-lang-completion.d.ts +37 -0
- package/out/lsp/domain-lang-completion.js +452 -0
- package/out/lsp/domain-lang-completion.js.map +1 -0
- package/out/lsp/domain-lang-formatter.d.ts +15 -0
- package/out/lsp/domain-lang-formatter.js +43 -0
- package/out/lsp/domain-lang-formatter.js.map +1 -0
- package/out/lsp/domain-lang-naming.d.ts +34 -0
- package/out/lsp/domain-lang-naming.js +49 -0
- package/out/lsp/domain-lang-naming.js.map +1 -0
- package/out/lsp/domain-lang-scope.d.ts +59 -0
- package/out/lsp/domain-lang-scope.js +102 -0
- package/out/lsp/domain-lang-scope.js.map +1 -0
- package/out/lsp/hover/ddd-pattern-explanations.d.ts +50 -0
- package/out/lsp/hover/ddd-pattern-explanations.js +196 -0
- package/out/lsp/hover/ddd-pattern-explanations.js.map +1 -0
- package/out/lsp/hover/domain-lang-hover.d.ts +19 -0
- package/out/lsp/hover/domain-lang-hover.js +306 -0
- package/out/lsp/hover/domain-lang-hover.js.map +1 -0
- package/out/lsp/hover/domain-lang-keywords.d.ts +13 -0
- package/out/lsp/hover/domain-lang-keywords.js +47 -0
- package/out/lsp/hover/domain-lang-keywords.js.map +1 -0
- package/out/main-browser.d.ts +1 -0
- package/out/main-browser.js +11 -0
- package/out/main-browser.js.map +1 -0
- package/out/main.d.ts +1 -0
- package/out/main.js +74 -0
- package/out/main.js.map +1 -0
- package/out/sdk/ast-augmentation.d.ts +136 -0
- package/out/sdk/ast-augmentation.js +62 -0
- package/out/sdk/ast-augmentation.js.map +1 -0
- package/out/sdk/index.d.ts +94 -0
- package/out/sdk/index.js +97 -0
- package/out/sdk/index.js.map +1 -0
- package/out/sdk/indexes.d.ts +16 -0
- package/out/sdk/indexes.js +97 -0
- package/out/sdk/indexes.js.map +1 -0
- package/out/sdk/loader-node.d.ts +47 -0
- package/out/sdk/loader-node.js +104 -0
- package/out/sdk/loader-node.js.map +1 -0
- package/out/sdk/loader.d.ts +49 -0
- package/out/sdk/loader.js +85 -0
- package/out/sdk/loader.js.map +1 -0
- package/out/sdk/patterns.d.ts +93 -0
- package/out/sdk/patterns.js +123 -0
- package/out/sdk/patterns.js.map +1 -0
- package/out/sdk/query.d.ts +90 -0
- package/out/sdk/query.js +679 -0
- package/out/sdk/query.js.map +1 -0
- package/out/sdk/resolution.d.ts +52 -0
- package/out/sdk/resolution.js +68 -0
- package/out/sdk/resolution.js.map +1 -0
- package/out/sdk/types.d.ts +301 -0
- package/out/sdk/types.js +8 -0
- package/out/sdk/types.js.map +1 -0
- package/out/services/dependency-analyzer.d.ts +94 -0
- package/out/services/dependency-analyzer.js +279 -0
- package/out/services/dependency-analyzer.js.map +1 -0
- package/out/services/dependency-resolver.d.ts +123 -0
- package/out/services/dependency-resolver.js +252 -0
- package/out/services/dependency-resolver.js.map +1 -0
- package/out/services/git-url-resolver.browser.d.ts +18 -0
- package/out/services/git-url-resolver.browser.js +15 -0
- package/out/services/git-url-resolver.browser.js.map +1 -0
- package/out/services/git-url-resolver.d.ts +192 -0
- package/out/services/git-url-resolver.js +382 -0
- package/out/services/git-url-resolver.js.map +1 -0
- package/out/services/governance-validator.d.ts +80 -0
- package/out/services/governance-validator.js +159 -0
- package/out/services/governance-validator.js.map +1 -0
- package/out/services/import-resolver.d.ts +18 -0
- package/out/services/import-resolver.js +22 -0
- package/out/services/import-resolver.js.map +1 -0
- package/out/services/performance-optimizer.d.ts +60 -0
- package/out/services/performance-optimizer.js +140 -0
- package/out/services/performance-optimizer.js.map +1 -0
- package/out/services/relationship-inference.d.ts +11 -0
- package/out/services/relationship-inference.js +98 -0
- package/out/services/relationship-inference.js.map +1 -0
- package/out/services/workspace-manager.d.ts +76 -0
- package/out/services/workspace-manager.js +323 -0
- package/out/services/workspace-manager.js.map +1 -0
- package/out/syntaxes/domain-lang.monarch.d.ts +76 -0
- package/out/syntaxes/domain-lang.monarch.js +29 -0
- package/out/syntaxes/domain-lang.monarch.js.map +1 -0
- package/out/utils/import-utils.d.ts +57 -0
- package/out/utils/import-utils.js +228 -0
- package/out/utils/import-utils.js.map +1 -0
- package/out/validation/bounded-context.d.ts +11 -0
- package/out/validation/bounded-context.js +79 -0
- package/out/validation/bounded-context.js.map +1 -0
- package/out/validation/classification.d.ts +3 -0
- package/out/validation/classification.js +3 -0
- package/out/validation/classification.js.map +1 -0
- package/out/validation/constants.d.ts +77 -0
- package/out/validation/constants.js +96 -0
- package/out/validation/constants.js.map +1 -0
- package/out/validation/domain-lang-validator.d.ts +2 -0
- package/out/validation/domain-lang-validator.js +27 -0
- package/out/validation/domain-lang-validator.js.map +1 -0
- package/out/validation/domain.d.ts +11 -0
- package/out/validation/domain.js +18 -0
- package/out/validation/domain.js.map +1 -0
- package/out/validation/import.d.ts +44 -0
- package/out/validation/import.js +135 -0
- package/out/validation/import.js.map +1 -0
- package/out/validation/maps.d.ts +21 -0
- package/out/validation/maps.js +56 -0
- package/out/validation/maps.js.map +1 -0
- package/out/validation/metadata.d.ts +7 -0
- package/out/validation/metadata.js +12 -0
- package/out/validation/metadata.js.map +1 -0
- package/out/validation/model.d.ts +12 -0
- package/out/validation/model.js +29 -0
- package/out/validation/model.js.map +1 -0
- package/out/validation/relationships.d.ts +12 -0
- package/out/validation/relationships.js +94 -0
- package/out/validation/relationships.js.map +1 -0
- package/out/validation/shared.d.ts +6 -0
- package/out/validation/shared.js +12 -0
- package/out/validation/shared.js.map +1 -0
- package/package.json +97 -0
- package/src/ast-augmentation.ts +5 -0
- package/src/domain-lang-module.ts +100 -0
- package/src/domain-lang.langium +356 -0
- package/src/generated/ast.ts +999 -0
- package/src/generated/grammar.ts +2504 -0
- package/src/generated/module.ts +25 -0
- package/src/index.ts +17 -0
- package/src/lsp/domain-lang-completion.ts +514 -0
- package/src/lsp/domain-lang-formatter.ts +51 -0
- package/src/lsp/domain-lang-naming.ts +56 -0
- package/src/lsp/domain-lang-scope.ts +137 -0
- package/src/lsp/hover/ddd-pattern-explanations.ts +237 -0
- package/src/lsp/hover/domain-lang-hover.ts +340 -0
- package/src/lsp/hover/domain-lang-keywords.ts +50 -0
- package/src/main-browser.ts +15 -0
- package/src/main.ts +85 -0
- package/src/sdk/README.md +297 -0
- package/src/sdk/ast-augmentation.ts +157 -0
- package/src/sdk/index.ts +128 -0
- package/src/sdk/indexes.ts +155 -0
- package/src/sdk/loader-node.ts +126 -0
- package/src/sdk/loader.ts +99 -0
- package/src/sdk/patterns.ts +147 -0
- package/src/sdk/query.ts +802 -0
- package/src/sdk/resolution.ts +78 -0
- package/src/sdk/types.ts +346 -0
- package/src/services/dependency-analyzer.ts +381 -0
- package/src/services/dependency-resolver.ts +334 -0
- package/src/services/git-url-resolver.browser.ts +31 -0
- package/src/services/git-url-resolver.ts +524 -0
- package/src/services/governance-validator.ts +219 -0
- package/src/services/import-resolver.ts +30 -0
- package/src/services/performance-optimizer.ts +170 -0
- package/src/services/relationship-inference.ts +121 -0
- package/src/services/workspace-manager.ts +416 -0
- package/src/syntaxes/domain-lang.monarch.ts +29 -0
- package/src/utils/import-utils.ts +274 -0
- package/src/validation/bounded-context.ts +99 -0
- package/src/validation/classification.ts +5 -0
- package/src/validation/constants.ts +124 -0
- package/src/validation/domain-lang-validator.ts +33 -0
- package/src/validation/domain.ts +24 -0
- package/src/validation/import.ts +171 -0
- package/src/validation/maps.ts +72 -0
- package/src/validation/metadata.ts +14 -0
- package/src/validation/model.ts +37 -0
- package/src/validation/relationships.ts +154 -0
- package/src/validation/shared.ts +14 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { URI } from 'langium';
|
|
4
|
+
import { GitUrlParser } from '../services/git-url-resolver.js';
|
|
5
|
+
import { WorkspaceManager } from '../services/workspace-manager.js';
|
|
6
|
+
// Singleton workspace manager instance
|
|
7
|
+
let workspaceManager;
|
|
8
|
+
/**
|
|
9
|
+
* Gets or creates the global workspace manager instance.
|
|
10
|
+
*
|
|
11
|
+
* @param startDir - Directory to start workspace search from
|
|
12
|
+
* @returns Promise resolving to the workspace manager
|
|
13
|
+
*/
|
|
14
|
+
async function getWorkspaceManager(startDir) {
|
|
15
|
+
if (!workspaceManager) {
|
|
16
|
+
workspaceManager = new WorkspaceManager();
|
|
17
|
+
const root = await findWorkspaceRoot(startDir);
|
|
18
|
+
try {
|
|
19
|
+
await workspaceManager.initialize(root);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.warn(`Failed to initialize workspace: ${error instanceof Error ? error.message : String(error)}`);
|
|
23
|
+
// Continue without workspace - local imports will still work
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return workspaceManager;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gets the git URL resolver from the workspace manager.
|
|
30
|
+
*
|
|
31
|
+
* @param startDir - Directory to start workspace search from
|
|
32
|
+
* @returns Promise resolving to the git URL resolver
|
|
33
|
+
*/
|
|
34
|
+
async function getGitResolver(startDir) {
|
|
35
|
+
const manager = await getWorkspaceManager(startDir);
|
|
36
|
+
return await manager.getGitResolver();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Ensures a file path has the .dlang extension.
|
|
40
|
+
*
|
|
41
|
+
* @param filePath - The raw file path
|
|
42
|
+
* @returns Normalized path with .dlang extension
|
|
43
|
+
* @throws {Error} If path has an invalid extension
|
|
44
|
+
*/
|
|
45
|
+
function ensureDlangExtension(filePath) {
|
|
46
|
+
const ext = path.extname(filePath);
|
|
47
|
+
if (!ext) {
|
|
48
|
+
return `${filePath}.dlang`;
|
|
49
|
+
}
|
|
50
|
+
if (ext !== '.dlang') {
|
|
51
|
+
throw new Error(`Invalid file extension: ${ext}. Expected .dlang for file: ${filePath}`);
|
|
52
|
+
}
|
|
53
|
+
return filePath;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolves workspace-relative paths (starting with ~/).
|
|
57
|
+
*
|
|
58
|
+
* @param importPath - Import path that may start with ~/
|
|
59
|
+
* @param workspaceRoot - The workspace root directory
|
|
60
|
+
* @returns Resolved absolute path
|
|
61
|
+
*/
|
|
62
|
+
function resolveWorkspacePath(importPath, workspaceRoot) {
|
|
63
|
+
if (importPath.startsWith('~/')) {
|
|
64
|
+
return path.join(workspaceRoot, importPath.slice(2));
|
|
65
|
+
}
|
|
66
|
+
return importPath;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Finds the workspace root by looking for common workspace indicators.
|
|
70
|
+
*
|
|
71
|
+
* Searches upward from the document's directory looking for:
|
|
72
|
+
* - dlang.toml
|
|
73
|
+
* - .git directory
|
|
74
|
+
* - package.json with dlang configuration
|
|
75
|
+
*
|
|
76
|
+
* @param startDir - Directory to start searching from
|
|
77
|
+
* @returns Workspace root path or the start directory if not found
|
|
78
|
+
*/
|
|
79
|
+
async function findWorkspaceRoot(startDir) {
|
|
80
|
+
let currentDir = startDir;
|
|
81
|
+
const root = path.parse(currentDir).root;
|
|
82
|
+
while (currentDir !== root) {
|
|
83
|
+
// Check for workspace indicators
|
|
84
|
+
const indicators = ['dlang.toml', '.git', 'package.json'];
|
|
85
|
+
for (const indicator of indicators) {
|
|
86
|
+
const indicatorPath = path.join(currentDir, indicator);
|
|
87
|
+
try {
|
|
88
|
+
await fs.access(indicatorPath);
|
|
89
|
+
return currentDir; // Found workspace root
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Continue searching
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Move up one directory
|
|
96
|
+
const parentDir = path.dirname(currentDir);
|
|
97
|
+
if (parentDir === currentDir)
|
|
98
|
+
break; // Reached root
|
|
99
|
+
currentDir = parentDir;
|
|
100
|
+
}
|
|
101
|
+
// Default to start directory if no workspace root found
|
|
102
|
+
return startDir;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Resolves an import path to an absolute file URI.
|
|
106
|
+
*
|
|
107
|
+
* Supports:
|
|
108
|
+
* - Local relative paths: ./file.dlang, ../other/file.dlang
|
|
109
|
+
* - Workspace-relative paths: ~/contexts/sales.dlang
|
|
110
|
+
* - Git URLs: gh:owner/repo@v1.0.0/file.dlang
|
|
111
|
+
* - Full URLs: https://github.com/owner/repo/blob/v1.0.0/file.dlang
|
|
112
|
+
*
|
|
113
|
+
* @param importingDoc - The document containing the import statement
|
|
114
|
+
* @param rawImportPath - The raw import path from the import statement
|
|
115
|
+
* @returns Resolved URI to the imported file
|
|
116
|
+
* @throws {Error} If the import cannot be resolved
|
|
117
|
+
*/
|
|
118
|
+
export async function resolveImportPath(importingDoc, rawImportPath) {
|
|
119
|
+
const baseDir = path.dirname(importingDoc.uri.fsPath);
|
|
120
|
+
// Handle manifest dependency aliases (friendly names)
|
|
121
|
+
const manager = await getWorkspaceManager(baseDir);
|
|
122
|
+
let gitResolver;
|
|
123
|
+
try {
|
|
124
|
+
const manifestImport = await manager.resolveDependencyImport(rawImportPath);
|
|
125
|
+
if (manifestImport) {
|
|
126
|
+
gitResolver = await manager.getGitResolver();
|
|
127
|
+
return await gitResolver.resolve(manifestImport);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Ignore manifest resolution issues; fall back to other strategies
|
|
132
|
+
}
|
|
133
|
+
// Handle git URLs (shorthand or full)
|
|
134
|
+
if (GitUrlParser.isGitUrl(rawImportPath)) {
|
|
135
|
+
gitResolver = gitResolver ?? await manager.getGitResolver();
|
|
136
|
+
return await gitResolver.resolve(rawImportPath);
|
|
137
|
+
}
|
|
138
|
+
// Handle workspace-relative paths (~/)
|
|
139
|
+
let resolvedPath = rawImportPath;
|
|
140
|
+
if (rawImportPath.startsWith('~/')) {
|
|
141
|
+
const workspaceRoot = await findWorkspaceRoot(baseDir);
|
|
142
|
+
resolvedPath = resolveWorkspacePath(rawImportPath, workspaceRoot);
|
|
143
|
+
}
|
|
144
|
+
else if (!path.isAbsolute(rawImportPath)) {
|
|
145
|
+
// Handle relative paths
|
|
146
|
+
resolvedPath = path.resolve(baseDir, rawImportPath);
|
|
147
|
+
}
|
|
148
|
+
// Ensure .dlang extension
|
|
149
|
+
const normalized = ensureDlangExtension(resolvedPath);
|
|
150
|
+
// Verify file exists
|
|
151
|
+
try {
|
|
152
|
+
await fs.access(normalized);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
throw new Error(`Import file not found: ${rawImportPath} (resolved to ${normalized})`);
|
|
156
|
+
}
|
|
157
|
+
return URI.file(normalized);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Legacy function for backward compatibility.
|
|
161
|
+
* Use resolveImportPath instead.
|
|
162
|
+
*
|
|
163
|
+
* @deprecated Use resolveImportPath which supports git URLs and workspace paths
|
|
164
|
+
*/
|
|
165
|
+
export async function resolveLocalImportPath(importingDoc, rawImportPath) {
|
|
166
|
+
const uri = await resolveImportPath(importingDoc, rawImportPath);
|
|
167
|
+
return uri.fsPath;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Ensures the import graph is loaded from an entry file.
|
|
171
|
+
*
|
|
172
|
+
* @param entryFilePath - Absolute or workspace-relative path to entry file
|
|
173
|
+
* @param langiumDocuments - The Langium documents manager
|
|
174
|
+
* @returns Set of URIs (as strings) for all documents in the import graph
|
|
175
|
+
* @throws {Error} If entry file cannot be resolved or loaded
|
|
176
|
+
*/
|
|
177
|
+
export async function ensureImportGraphFromEntryFile(entryFilePath, langiumDocuments) {
|
|
178
|
+
const entryUri = URI.file(path.resolve(entryFilePath));
|
|
179
|
+
const entryDoc = await langiumDocuments.getOrCreateDocument(entryUri);
|
|
180
|
+
return ensureImportGraphFromDocument(entryDoc, langiumDocuments);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Recursively builds the import graph from a document.
|
|
184
|
+
*
|
|
185
|
+
* @param document - The starting document
|
|
186
|
+
* @param langiumDocuments - The Langium documents manager
|
|
187
|
+
* @returns Set of URIs (as strings) for all documents in the import graph
|
|
188
|
+
*/
|
|
189
|
+
export async function ensureImportGraphFromDocument(document, langiumDocuments) {
|
|
190
|
+
const visited = new Set();
|
|
191
|
+
async function visit(doc) {
|
|
192
|
+
const uriString = doc.uri.toString();
|
|
193
|
+
if (visited.has(uriString))
|
|
194
|
+
return;
|
|
195
|
+
visited.add(uriString);
|
|
196
|
+
const model = doc.parseResult.value;
|
|
197
|
+
for (const imp of model.imports ?? []) {
|
|
198
|
+
if (!imp.uri)
|
|
199
|
+
continue;
|
|
200
|
+
// Use new resolveImportPath that supports git URLs
|
|
201
|
+
const resolvedUri = await resolveImportPath(doc, imp.uri);
|
|
202
|
+
const childDoc = await langiumDocuments.getOrCreateDocument(resolvedUri);
|
|
203
|
+
await visit(childDoc);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
await visit(document);
|
|
207
|
+
return visited;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Gets cache statistics for git imports.
|
|
211
|
+
*
|
|
212
|
+
* @returns Cache statistics including size and number of cached repositories
|
|
213
|
+
*/
|
|
214
|
+
export async function getGitCacheStats(startDir = process.cwd()) {
|
|
215
|
+
const resolver = await getGitResolver(startDir);
|
|
216
|
+
return await resolver.getCacheStats();
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Clears the git import cache.
|
|
220
|
+
*
|
|
221
|
+
* @param startDir - Starting directory for workspace resolution
|
|
222
|
+
* @returns Promise that resolves when cache is cleared
|
|
223
|
+
*/
|
|
224
|
+
export async function clearGitCache(startDir = process.cwd()) {
|
|
225
|
+
const resolver = await getGitResolver(startDir);
|
|
226
|
+
return await resolver.clearCache();
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=import-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-utils.js","sourceRoot":"","sources":["../../src/utils/import-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ValidationAcceptor } from 'langium';
|
|
2
|
+
import type { BoundedContext } from '../generated/ast.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validates that a bounded context has a description.
|
|
5
|
+
*
|
|
6
|
+
* @param bc - The bounded context to validate
|
|
7
|
+
* @param accept - The validation acceptor for reporting issues
|
|
8
|
+
*/
|
|
9
|
+
declare function validateBoundedContextHasDescription(bc: BoundedContext, accept: ValidationAcceptor): void;
|
|
10
|
+
export declare const boundedContextChecks: (typeof validateBoundedContextHasDescription)[];
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ValidationMessages, buildCodeDescription } from './constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validates that a bounded context has a description.
|
|
4
|
+
*
|
|
5
|
+
* @param bc - The bounded context to validate
|
|
6
|
+
* @param accept - The validation acceptor for reporting issues
|
|
7
|
+
*/
|
|
8
|
+
function validateBoundedContextHasDescription(bc, accept) {
|
|
9
|
+
if (!bc.description) {
|
|
10
|
+
accept('warning', ValidationMessages.BOUNDED_CONTEXT_NO_DESCRIPTION(bc.name), {
|
|
11
|
+
node: bc,
|
|
12
|
+
keyword: 'BoundedContext',
|
|
13
|
+
codeDescription: buildCodeDescription('language.md', 'bounded-contexts')
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Validates that a bounded context belongs to a domain.
|
|
19
|
+
* A BoundedContext must have a 'for Domain' clause.
|
|
20
|
+
*
|
|
21
|
+
* @param bc - The bounded context to validate
|
|
22
|
+
* @param accept - The validation acceptor for reporting issues
|
|
23
|
+
*/
|
|
24
|
+
function validateBoundedContextHasDomain(bc, accept) {
|
|
25
|
+
if (!bc.domain) {
|
|
26
|
+
accept('warning', ValidationMessages.BOUNDED_CONTEXT_NO_DOMAIN(bc.name), {
|
|
27
|
+
node: bc,
|
|
28
|
+
keyword: 'for',
|
|
29
|
+
codeDescription: buildCodeDescription('language.md', 'bounded-contexts')
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validates conflicts between inline and block classification assignment.
|
|
35
|
+
* Warns when both inline ('as') and block ('classification:') are used.
|
|
36
|
+
* Inline values take precedence per PRS-008.
|
|
37
|
+
*
|
|
38
|
+
* FR-9.2: Inline/Block Conflict Validation
|
|
39
|
+
*/
|
|
40
|
+
function validateBoundedContextClassificationConflict(bc, accept) {
|
|
41
|
+
if (bc.classification.length > 1) {
|
|
42
|
+
const inlineClassificationName = bc.classification[0].ref?.name;
|
|
43
|
+
const blockClassificationName = bc.classification[1].ref?.name;
|
|
44
|
+
// Warn if defined multiple times
|
|
45
|
+
accept('warning', ValidationMessages.BOUNDED_CONTEXT_CLASSIFICATION_CONFLICT(bc.name, inlineClassificationName, blockClassificationName), {
|
|
46
|
+
node: bc,
|
|
47
|
+
property: 'classification',
|
|
48
|
+
index: 1,
|
|
49
|
+
codeDescription: buildCodeDescription('language.md', 'bounded-contexts')
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validates conflicts between inline and block team assignment.
|
|
55
|
+
* Warns when both inline ('by') and block ('team:') are used.
|
|
56
|
+
* Inline values take precedence per PRS-008.
|
|
57
|
+
*
|
|
58
|
+
* FR-2.3: Inline/Block Conflict Validation
|
|
59
|
+
*/
|
|
60
|
+
function validateBoundedContextTeamConflict(bc, accept) {
|
|
61
|
+
if (bc.team.length > 1) {
|
|
62
|
+
const inlineTeamName = bc.team[0].ref?.name;
|
|
63
|
+
const blockTeamName = bc.team[1].ref?.name;
|
|
64
|
+
// Warn if defined multiple times
|
|
65
|
+
accept('warning', ValidationMessages.BOUNDED_CONTEXT_TEAM_CONFLICT(bc.name, inlineTeamName, blockTeamName), {
|
|
66
|
+
node: bc,
|
|
67
|
+
property: 'team',
|
|
68
|
+
index: 1,
|
|
69
|
+
codeDescription: buildCodeDescription('language.md', 'bounded-contexts')
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export const boundedContextChecks = [
|
|
74
|
+
validateBoundedContextHasDescription,
|
|
75
|
+
validateBoundedContextHasDomain,
|
|
76
|
+
validateBoundedContextClassificationConflict,
|
|
77
|
+
validateBoundedContextTeamConflict
|
|
78
|
+
];
|
|
79
|
+
//# sourceMappingURL=bounded-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounded-context.js","sourceRoot":"","sources":["../../src/validation/bounded-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;;;GAKG;AACH,SAAS,oCAAoC,CACzC,EAAkB,EAClB,MAA0B;IAE1B,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,8BAA8B,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;YAC1E,IAAI,EAAE,EAAE;YACR,OAAO,EAAE,gBAAgB;YACzB,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;SAC3E,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,+BAA+B,CACpC,EAAkB,EAClB,MAA0B;IAE1B,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,yBAAyB,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE;YACrE,IAAI,EAAE,EAAE;YACR,OAAO,EAAE,KAAK;YACd,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;SAC3E,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,4CAA4C,CACjD,EAAkB,EAClB,MAA0B;IAE1B,IAAI,EAAE,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,wBAAwB,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;QAChE,MAAM,uBAAuB,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;QAE/D,iCAAiC;QACjC,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,uCAAuC,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,EAAE,uBAAuB,CAAC,EAAE;YACtI,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,gBAAgB;YAC1B,KAAK,EAAE,CAAC;YACR,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;SAC3E,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kCAAkC,CACvC,EAAkB,EAClB,MAA0B;IAE1B,IAAI,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,cAAc,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;QAC5C,MAAM,aAAa,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;QAE3C,iCAAiC;QACjC,MAAM,CAAC,SAAS,EAAE,kBAAkB,CAAC,6BAA6B,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE;YACxG,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC;YACR,eAAe,EAAE,oBAAoB,CAAC,aAAa,EAAE,kBAAkB,CAAC;SAC3E,CAAC,CAAC;IACP,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAChC,oCAAoC;IACpC,+BAA+B;IAC/B,4CAA4C;IAC5C,kCAAkC;CACrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification.js","sourceRoot":"","sources":["../../src/validation/classification.ts"],"names":[],"mappings":"AAGA,4DAA4D;AAC5D,MAAM,CAAC,MAAM,oBAAoB,GAAsC,EAAE,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
import type { CodeDescription } from 'vscode-languageserver-types';
|
|
14
|
+
/**
|
|
15
|
+
* Creates a CodeDescription for clickable documentation links in VS Code.
|
|
16
|
+
* @param docPath - Relative path from docs/ folder
|
|
17
|
+
* @param anchor - Optional section anchor (without #)
|
|
18
|
+
*/
|
|
19
|
+
export declare const buildCodeDescription: (docPath: string, anchor?: string) => CodeDescription;
|
|
20
|
+
export declare const ValidationMessages: {
|
|
21
|
+
/**
|
|
22
|
+
* Warning message when a domain lacks a vision statement.
|
|
23
|
+
* @param name - The name of the domain
|
|
24
|
+
*/
|
|
25
|
+
readonly DOMAIN_NO_VISION: (name: string) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Warning message when a bounded context lacks a description.
|
|
28
|
+
* @param name - The name of the bounded context
|
|
29
|
+
*/
|
|
30
|
+
readonly BOUNDED_CONTEXT_NO_DESCRIPTION: (name: string) => string;
|
|
31
|
+
/**
|
|
32
|
+
* Warning message when a bounded context lacks a domain reference.
|
|
33
|
+
* @param name - The name of the bounded context
|
|
34
|
+
*/
|
|
35
|
+
readonly BOUNDED_CONTEXT_NO_DOMAIN: (name: string) => string;
|
|
36
|
+
/**
|
|
37
|
+
* Warning when classification is specified both inline and in a block.
|
|
38
|
+
* Inline value takes precedence.
|
|
39
|
+
* @param bcName - The name of the bounded context
|
|
40
|
+
* @param inlineClassification - The inline classification name (from 'as')
|
|
41
|
+
* @param blockClassification - The block classification name (from 'classification:')
|
|
42
|
+
*/
|
|
43
|
+
readonly BOUNDED_CONTEXT_CLASSIFICATION_CONFLICT: (bcName: string, inlineClassification?: string, blockClassification?: string) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Warning when team is specified both inline and in a block.
|
|
46
|
+
* Inline value takes precedence.
|
|
47
|
+
* @param bcName - The name of the bounded context
|
|
48
|
+
* @param inlineTeam - The inline team name (from 'by')
|
|
49
|
+
* @param blockTeam - The block team name (from 'team:')
|
|
50
|
+
*/
|
|
51
|
+
readonly BOUNDED_CONTEXT_TEAM_CONFLICT: (bcName: string, inlineTeam?: string, blockTeam?: string) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Error message when an element is defined multiple times.
|
|
54
|
+
* @param fqn - The fully qualified name of the duplicate element
|
|
55
|
+
*/
|
|
56
|
+
readonly DUPLICATE_ELEMENT: (fqn: string) => string;
|
|
57
|
+
/**
|
|
58
|
+
* Warning when SharedKernel pattern uses incorrect arrow direction.
|
|
59
|
+
* SharedKernel requires bidirectional relationship.
|
|
60
|
+
*/
|
|
61
|
+
readonly SHARED_KERNEL_MUST_BE_BIDIRECTIONAL: (leftContext: string, rightContext: string, arrow: string) => string;
|
|
62
|
+
/**
|
|
63
|
+
* Warning when Anti-Corruption Layer is on the wrong side of relationship.
|
|
64
|
+
* ACL should protect the consuming context (downstream).
|
|
65
|
+
*/
|
|
66
|
+
readonly ACL_ON_WRONG_SIDE: (context: string, side: "left" | "right") => string;
|
|
67
|
+
/**
|
|
68
|
+
* Warning when Conformist pattern is on the wrong side.
|
|
69
|
+
* Conformist accepts upstream model without translation.
|
|
70
|
+
*/
|
|
71
|
+
readonly CONFORMIST_ON_WRONG_SIDE: (context: string, side: "left" | "right") => string;
|
|
72
|
+
/**
|
|
73
|
+
* Info message when relationship has too many integration patterns.
|
|
74
|
+
* Suggests possible syntax confusion.
|
|
75
|
+
*/
|
|
76
|
+
readonly TOO_MANY_PATTERNS: (count: number, side: "left" | "right") => string;
|
|
77
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
// Documentation Link Utilities
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const REPO_BASE = 'https://github.com/larsbaunwall/DomainLang/blob/main';
|
|
17
|
+
const DOCS_BASE = `${REPO_BASE}/dsl/domain-lang/docs`;
|
|
18
|
+
/**
|
|
19
|
+
* Builds a documentation URL for error messages.
|
|
20
|
+
* @param docPath - Relative path from docs/ folder
|
|
21
|
+
* @param anchor - Optional section anchor (without #)
|
|
22
|
+
*/
|
|
23
|
+
const buildDocLink = (docPath, anchor) => `${DOCS_BASE}/${docPath}${anchor ? `#${anchor}` : ''}`;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a CodeDescription for clickable documentation links in VS Code.
|
|
26
|
+
* @param docPath - Relative path from docs/ folder
|
|
27
|
+
* @param anchor - Optional section anchor (without #)
|
|
28
|
+
*/
|
|
29
|
+
export const buildCodeDescription = (docPath, anchor) => ({
|
|
30
|
+
href: buildDocLink(docPath, anchor)
|
|
31
|
+
});
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Enhanced Validation Messages
|
|
34
|
+
// ============================================================================
|
|
35
|
+
export const ValidationMessages = {
|
|
36
|
+
/**
|
|
37
|
+
* Warning message when a domain lacks a vision statement.
|
|
38
|
+
* @param name - The name of the domain
|
|
39
|
+
*/
|
|
40
|
+
DOMAIN_NO_VISION: (name) => `Domain '${name}' is missing a vision statement.`,
|
|
41
|
+
/**
|
|
42
|
+
* Warning message when a bounded context lacks a description.
|
|
43
|
+
* @param name - The name of the bounded context
|
|
44
|
+
*/
|
|
45
|
+
BOUNDED_CONTEXT_NO_DESCRIPTION: (name) => `Bounded Context '${name}' is missing a description.`,
|
|
46
|
+
/**
|
|
47
|
+
* Warning message when a bounded context lacks a domain reference.
|
|
48
|
+
* @param name - The name of the bounded context
|
|
49
|
+
*/
|
|
50
|
+
BOUNDED_CONTEXT_NO_DOMAIN: (name) => `Bounded Context '${name}' must belong to a domain. Use 'for DomainName'.`,
|
|
51
|
+
/**
|
|
52
|
+
* Warning when classification is specified both inline and in a block.
|
|
53
|
+
* Inline value takes precedence.
|
|
54
|
+
* @param bcName - The name of the bounded context
|
|
55
|
+
* @param inlineClassification - The inline classification name (from 'as')
|
|
56
|
+
* @param blockClassification - The block classification name (from 'classification:')
|
|
57
|
+
*/
|
|
58
|
+
BOUNDED_CONTEXT_CLASSIFICATION_CONFLICT: (bcName, inlineClassification, blockClassification) => `Classification specified both inline${inlineClassification ? ` ('as ${inlineClassification}')` : ''} and in block${blockClassification ? ` ('classification: ${blockClassification}')` : ''}. Inline value takes precedence.`,
|
|
59
|
+
/**
|
|
60
|
+
* Warning when team is specified both inline and in a block.
|
|
61
|
+
* Inline value takes precedence.
|
|
62
|
+
* @param bcName - The name of the bounded context
|
|
63
|
+
* @param inlineTeam - The inline team name (from 'by')
|
|
64
|
+
* @param blockTeam - The block team name (from 'team:')
|
|
65
|
+
*/
|
|
66
|
+
BOUNDED_CONTEXT_TEAM_CONFLICT: (bcName, inlineTeam, blockTeam) => `Team specified both inline${inlineTeam ? ` ('by ${inlineTeam}')` : ''} and in block${blockTeam ? ` ('team: ${blockTeam}')` : ''}. Inline value takes precedence.`,
|
|
67
|
+
/**
|
|
68
|
+
* Error message when an element is defined multiple times.
|
|
69
|
+
* @param fqn - The fully qualified name of the duplicate element
|
|
70
|
+
*/
|
|
71
|
+
DUPLICATE_ELEMENT: (fqn) => `Duplicate element: '${fqn}' is already defined.`,
|
|
72
|
+
// ========================================================================
|
|
73
|
+
// Integration Pattern & Relationship Validation
|
|
74
|
+
// ========================================================================
|
|
75
|
+
/**
|
|
76
|
+
* Warning when SharedKernel pattern uses incorrect arrow direction.
|
|
77
|
+
* SharedKernel requires bidirectional relationship.
|
|
78
|
+
*/
|
|
79
|
+
SHARED_KERNEL_MUST_BE_BIDIRECTIONAL: (leftContext, rightContext, arrow) => `SharedKernel between '${leftContext}' and '${rightContext}' requires bidirectional arrow '<->', not '${arrow}'.`,
|
|
80
|
+
/**
|
|
81
|
+
* Warning when Anti-Corruption Layer is on the wrong side of relationship.
|
|
82
|
+
* ACL should protect the consuming context (downstream).
|
|
83
|
+
*/
|
|
84
|
+
ACL_ON_WRONG_SIDE: (context, side) => `Anti-Corruption Layer (ACL) on '${context}' should be on downstream (consuming) side, not ${side} side.`,
|
|
85
|
+
/**
|
|
86
|
+
* Warning when Conformist pattern is on the wrong side.
|
|
87
|
+
* Conformist accepts upstream model without translation.
|
|
88
|
+
*/
|
|
89
|
+
CONFORMIST_ON_WRONG_SIDE: (context, side) => `Conformist (CF) on '${context}' should be on downstream (consuming) side, not ${side} side.`,
|
|
90
|
+
/**
|
|
91
|
+
* Info message when relationship has too many integration patterns.
|
|
92
|
+
* Suggests possible syntax confusion.
|
|
93
|
+
*/
|
|
94
|
+
TOO_MANY_PATTERNS: (count, side) => `Too many integration patterns (${count}) on ${side} side. Typically use 1-2 patterns per side.`
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/validation/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,+EAA+E;AAC/E,+BAA+B;AAC/B,+EAA+E;AAE/E,MAAM,SAAS,GAAG,sDAAsD,CAAC;AACzE,MAAM,SAAS,GAAG,GAAG,SAAS,uBAAuB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,YAAY,GAAG,CAAC,OAAe,EAAE,MAAe,EAAU,EAAE,CAC9D,GAAG,SAAS,IAAI,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAE3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAe,EAAE,MAAe,EAAmB,EAAE,CAAC,CAAC;IACxF,IAAI,EAAE,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC;CACtC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,+BAA+B;AAC/B,+EAA+E;AAE/E,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAC9B;;;OAGG;IACH,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE,CAC/B,WAAW,IAAI,kCAAkC;IAErD;;;OAGG;IACH,8BAA8B,EAAE,CAAC,IAAY,EAAE,EAAE,CAC7C,oBAAoB,IAAI,6BAA6B;IAEzD;;;OAGG;IACH,yBAAyB,EAAE,CAAC,IAAY,EAAE,EAAE,CACxC,oBAAoB,IAAI,kDAAkD;IAE9E;;;;;;OAMG;IACH,uCAAuC,EAAE,CAAC,MAAc,EAAE,oBAA6B,EAAE,mBAA4B,EAAE,EAAE,CACrH,uCAAuC,oBAAoB,CAAC,CAAC,CAAC,SAAS,oBAAoB,IAAI,CAAC,CAAC,CAAC,EAAE,gBAAgB,mBAAmB,CAAC,CAAC,CAAC,sBAAsB,mBAAmB,IAAI,CAAC,CAAC,CAAC,EAAE,kCAAkC;IAElO;;;;;;OAMG;IACH,6BAA6B,EAAE,CAAC,MAAc,EAAE,UAAmB,EAAE,SAAkB,EAAE,EAAE,CACvF,6BAA6B,UAAU,CAAC,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,CAAC,CAAC,EAAE,gBAAgB,SAAS,CAAC,CAAC,CAAC,YAAY,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,kCAAkC;IAEtK;;;OAGG;IACH,iBAAiB,EAAE,CAAC,GAAW,EAAE,EAAE,CAC/B,uBAAuB,GAAG,uBAAuB;IAErD,2EAA2E;IAC3E,gDAAgD;IAChD,2EAA2E;IAE3E;;;OAGG;IACH,mCAAmC,EAAE,CAAC,WAAmB,EAAE,YAAoB,EAAE,KAAa,EAAE,EAAE,CAC9F,yBAAyB,WAAW,UAAU,YAAY,8CAA8C,KAAK,IAAI;IAErH;;;OAGG;IACH,iBAAiB,EAAE,CAAC,OAAe,EAAE,IAAsB,EAAE,EAAE,CAC3D,mCAAmC,OAAO,mDAAmD,IAAI,QAAQ;IAE7G;;;OAGG;IACH,wBAAwB,EAAE,CAAC,OAAe,EAAE,IAAsB,EAAE,EAAE,CAClE,uBAAuB,OAAO,mDAAmD,IAAI,QAAQ;IAEjG;;;OAGG;IACH,iBAAiB,EAAE,CAAC,KAAa,EAAE,IAAsB,EAAE,EAAE,CACzD,kCAAkC,KAAK,QAAQ,IAAI,6CAA6C;CAC9F,CAAC"}
|
|
@@ -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,18 @@
|
|
|
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
|
+
export const domainChecks = [validateDomainHasVision];
|
|
18
|
+
//# 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,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,uBAAuB,CAAC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ValidationAcceptor, ValidationChecks } from 'langium';
|
|
2
|
+
import type { DomainLangAstType, ImportStatement, Model } from '../generated/ast.js';
|
|
3
|
+
import type { DomainLangServices } from '../domain-lang-module.js';
|
|
4
|
+
import type { LangiumDocument } from 'langium';
|
|
5
|
+
/**
|
|
6
|
+
* Validates import statements in DomainLang.
|
|
7
|
+
*
|
|
8
|
+
* Checks:
|
|
9
|
+
* - Import paths are resolvable
|
|
10
|
+
* - Named imports exist in target document
|
|
11
|
+
* - Import aliases don't conflict with local names
|
|
12
|
+
*/
|
|
13
|
+
export declare class ImportValidator {
|
|
14
|
+
private readonly documents;
|
|
15
|
+
constructor(services: DomainLangServices);
|
|
16
|
+
/**
|
|
17
|
+
* Validates that an import path is resolvable.
|
|
18
|
+
*/
|
|
19
|
+
checkImportPath(imp: ImportStatement, accept: ValidationAcceptor, document: LangiumDocument): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Validates that named imports exist in the target document.
|
|
22
|
+
*/
|
|
23
|
+
checkNamedImports(imp: ImportStatement, accept: ValidationAcceptor, document: LangiumDocument): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Gets all exportable symbols from a model.
|
|
26
|
+
*
|
|
27
|
+
* In DomainLang, top-level declarations are implicitly exported:
|
|
28
|
+
* - Domains
|
|
29
|
+
* - BoundedContexts
|
|
30
|
+
* - Classifications
|
|
31
|
+
* - Groups
|
|
32
|
+
*/
|
|
33
|
+
private getExportedSymbols;
|
|
34
|
+
/**
|
|
35
|
+
* Checks for unused imports.
|
|
36
|
+
*
|
|
37
|
+
* This is a warning, not an error, to avoid being too strict.
|
|
38
|
+
*/
|
|
39
|
+
checkUnusedImports(imp: ImportStatement, _accept: ValidationAcceptor, _model: Model): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates validation checks for import statements.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createImportChecks(_services: DomainLangServices): ValidationChecks<DomainLangAstType>;
|