@domainlang/language 0.1.82 → 0.4.1
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 +18 -18
- package/out/domain-lang-module.d.ts +2 -0
- package/out/domain-lang-module.js +11 -3
- package/out/domain-lang-module.js.map +1 -1
- package/out/generated/ast.d.ts +8 -19
- package/out/generated/ast.js +1 -10
- package/out/generated/ast.js.map +1 -1
- package/out/generated/grammar.d.ts +1 -1
- package/out/generated/grammar.js +28 -123
- package/out/generated/grammar.js.map +1 -1
- package/out/generated/module.d.ts +1 -1
- package/out/generated/module.js +1 -1
- package/out/index.d.ts +3 -0
- package/out/index.js +5 -0
- package/out/index.js.map +1 -1
- package/out/lsp/domain-lang-code-actions.d.ts +55 -0
- package/out/lsp/domain-lang-code-actions.js +143 -0
- package/out/lsp/domain-lang-code-actions.js.map +1 -0
- package/out/lsp/domain-lang-workspace-manager.d.ts +21 -0
- package/out/lsp/domain-lang-workspace-manager.js +93 -0
- package/out/lsp/domain-lang-workspace-manager.js.map +1 -0
- package/out/lsp/hover/domain-lang-hover.js +0 -4
- package/out/lsp/hover/domain-lang-hover.js.map +1 -1
- package/out/lsp/manifest-diagnostics.d.ts +82 -0
- package/out/lsp/manifest-diagnostics.js +230 -0
- package/out/lsp/manifest-diagnostics.js.map +1 -0
- package/out/sdk/index.d.ts +1 -1
- package/out/sdk/loader-node.d.ts +7 -3
- package/out/sdk/loader-node.js +24 -9
- package/out/sdk/loader-node.js.map +1 -1
- package/out/sdk/types.d.ts +0 -21
- package/out/services/dependency-analyzer.d.ts +3 -39
- package/out/services/dependency-analyzer.js +22 -47
- package/out/services/dependency-analyzer.js.map +1 -1
- package/out/services/dependency-resolver.d.ts +68 -45
- package/out/services/dependency-resolver.js +243 -43
- package/out/services/dependency-resolver.js.map +1 -1
- package/out/services/git-url-resolver.browser.d.ts +4 -12
- package/out/services/git-url-resolver.browser.js +5 -1
- package/out/services/git-url-resolver.browser.js.map +1 -1
- package/out/services/git-url-resolver.d.ts +22 -56
- package/out/services/git-url-resolver.js +70 -36
- package/out/services/git-url-resolver.js.map +1 -1
- package/out/services/governance-validator.d.ts +1 -37
- package/out/services/governance-validator.js +4 -10
- package/out/services/governance-validator.js.map +1 -1
- package/out/services/import-resolver.d.ts +65 -6
- package/out/services/import-resolver.js +223 -5
- package/out/services/import-resolver.js.map +1 -1
- package/out/services/performance-optimizer.d.ts +1 -1
- package/out/services/semver.d.ts +98 -0
- package/out/services/semver.js +195 -0
- package/out/services/semver.js.map +1 -0
- package/out/services/types.d.ts +340 -0
- package/out/services/types.js +46 -0
- package/out/services/types.js.map +1 -0
- package/out/services/workspace-manager.d.ts +57 -10
- package/out/services/workspace-manager.js +187 -21
- package/out/services/workspace-manager.js.map +1 -1
- package/out/syntaxes/domain-lang.monarch.js +1 -1
- package/out/syntaxes/domain-lang.monarch.js.map +1 -1
- package/out/utils/import-utils.d.ts +4 -12
- package/out/utils/import-utils.js +35 -135
- package/out/utils/import-utils.js.map +1 -1
- package/out/validation/constants.d.ts +103 -0
- package/out/validation/constants.js +141 -2
- package/out/validation/constants.js.map +1 -1
- package/out/validation/domain.js +46 -1
- package/out/validation/domain.js.map +1 -1
- package/out/validation/import.d.ts +46 -22
- package/out/validation/import.js +187 -85
- package/out/validation/import.js.map +1 -1
- package/out/validation/manifest.d.ts +144 -0
- package/out/validation/manifest.js +327 -0
- package/out/validation/manifest.js.map +1 -0
- package/out/validation/maps.js +10 -6
- package/out/validation/maps.js.map +1 -1
- package/out/validation/metadata.js +5 -1
- package/out/validation/metadata.js.map +1 -1
- package/package.json +8 -6
- package/src/domain-lang-module.ts +18 -6
- package/src/domain-lang.langium +7 -12
- package/src/generated/ast.ts +7 -20
- package/src/generated/grammar.ts +28 -123
- package/src/generated/module.ts +1 -1
- package/src/index.ts +7 -0
- package/src/lsp/domain-lang-code-actions.ts +189 -0
- package/src/lsp/domain-lang-workspace-manager.ts +104 -0
- package/src/lsp/hover/domain-lang-hover.ts +0 -2
- package/src/lsp/manifest-diagnostics.ts +290 -0
- package/src/sdk/index.ts +0 -2
- package/src/sdk/loader-node.ts +29 -9
- package/src/sdk/types.ts +0 -23
- package/src/services/dependency-analyzer.ts +24 -84
- package/src/services/dependency-resolver.ts +301 -84
- package/src/services/git-url-resolver.browser.ts +9 -14
- package/src/services/git-url-resolver.ts +86 -93
- package/src/services/governance-validator.ts +5 -47
- package/src/services/import-resolver.ts +270 -8
- package/src/services/performance-optimizer.ts +1 -1
- package/src/services/semver.ts +213 -0
- package/src/services/types.ts +415 -0
- package/src/services/workspace-manager.ts +237 -46
- package/src/utils/import-utils.ts +38 -160
- package/src/validation/constants.ts +182 -2
- package/src/validation/domain.ts +54 -1
- package/src/validation/import.ts +228 -104
- package/src/validation/manifest.ts +439 -0
- package/src/validation/maps.ts +10 -6
- package/src/validation/metadata.ts +5 -1
- package/src/syntaxes/domain-lang.monarch.ts +0 -29
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest Diagnostics Service for DomainLang.
|
|
3
|
+
*
|
|
4
|
+
* Provides LSP diagnostics for model.yaml files by integrating the ManifestValidator
|
|
5
|
+
* with the VS Code language server protocol.
|
|
6
|
+
*
|
|
7
|
+
* This service:
|
|
8
|
+
* - Validates model.yaml files using ManifestValidator
|
|
9
|
+
* - Converts ManifestDiagnostic to LSP Diagnostic format
|
|
10
|
+
* - Sends diagnostics to the LSP connection
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Connection } from 'vscode-languageserver';
|
|
16
|
+
import { Diagnostic, DiagnosticSeverity, Position, Range } from 'vscode-languageserver-types';
|
|
17
|
+
import YAML, { type Document as YAMLDocument, type YAMLMap, type Pair, isMap, isPair, isScalar } from 'yaml';
|
|
18
|
+
import { ManifestValidator, type ManifestDiagnostic, type ManifestSeverity } from '../validation/manifest.js';
|
|
19
|
+
import type { ModelManifest } from '../services/types.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Service for validating model.yaml and sending diagnostics via LSP.
|
|
23
|
+
*/
|
|
24
|
+
export class ManifestDiagnosticsService {
|
|
25
|
+
private readonly validator = new ManifestValidator();
|
|
26
|
+
private connection: Connection | undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sets the LSP connection for sending diagnostics.
|
|
30
|
+
* Must be called before validateAndSendDiagnostics.
|
|
31
|
+
*/
|
|
32
|
+
setConnection(connection: Connection): void {
|
|
33
|
+
this.connection = connection;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validates a model.yaml file and sends diagnostics to the LSP connection.
|
|
38
|
+
*
|
|
39
|
+
* @param manifestUri - URI of the model.yaml file
|
|
40
|
+
* @param content - Raw YAML content of the file
|
|
41
|
+
* @param options - Validation options
|
|
42
|
+
*/
|
|
43
|
+
async validateAndSendDiagnostics(
|
|
44
|
+
manifestUri: string,
|
|
45
|
+
content: string,
|
|
46
|
+
options?: { requirePublishable?: boolean }
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
if (!this.connection) {
|
|
49
|
+
return; // No connection, skip diagnostics
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const diagnostics = this.validate(content, options);
|
|
53
|
+
|
|
54
|
+
await this.connection.sendDiagnostics({
|
|
55
|
+
uri: manifestUri,
|
|
56
|
+
diagnostics
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validates manifest content and returns LSP diagnostics.
|
|
62
|
+
*
|
|
63
|
+
* @param content - Raw YAML content
|
|
64
|
+
* @param options - Validation options
|
|
65
|
+
* @returns Array of LSP diagnostics
|
|
66
|
+
*/
|
|
67
|
+
validate(
|
|
68
|
+
content: string,
|
|
69
|
+
options?: { requirePublishable?: boolean }
|
|
70
|
+
): Diagnostic[] {
|
|
71
|
+
// Parse YAML to get both the manifest object and source map
|
|
72
|
+
let yamlDoc: YAMLDocument.Parsed;
|
|
73
|
+
let manifest: ModelManifest;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
yamlDoc = YAML.parseDocument(content);
|
|
77
|
+
|
|
78
|
+
// Check for YAML parse errors (they're in the errors array, not thrown)
|
|
79
|
+
if (yamlDoc.errors && yamlDoc.errors.length > 0) {
|
|
80
|
+
return yamlDoc.errors.map(err => ({
|
|
81
|
+
severity: DiagnosticSeverity.Error,
|
|
82
|
+
range: this.yamlErrorToRange(err, content),
|
|
83
|
+
message: `YAML parse error: ${err.message}`,
|
|
84
|
+
source: 'domainlang'
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
manifest = (yamlDoc.toJSON() ?? {}) as ModelManifest;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
// Fallback for unexpected errors
|
|
91
|
+
const message = error instanceof Error ? error.message : 'Invalid YAML syntax';
|
|
92
|
+
return [{
|
|
93
|
+
severity: DiagnosticSeverity.Error,
|
|
94
|
+
range: Range.create(Position.create(0, 0), Position.create(0, 1)),
|
|
95
|
+
message: `YAML parse error: ${message}`,
|
|
96
|
+
source: 'domainlang'
|
|
97
|
+
}];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Run manifest validation
|
|
101
|
+
const result = this.validator.validate(manifest, options);
|
|
102
|
+
|
|
103
|
+
// Convert to LSP diagnostics with source locations
|
|
104
|
+
return result.diagnostics.map(diag =>
|
|
105
|
+
this.toVSCodeDiagnostic(diag, yamlDoc)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Converts a YAML error to an LSP Range.
|
|
111
|
+
*/
|
|
112
|
+
private yamlErrorToRange(err: YAML.YAMLError, _content: string): Range {
|
|
113
|
+
if (err.linePos && err.linePos.length >= 1) {
|
|
114
|
+
const startPos = err.linePos[0];
|
|
115
|
+
const startLine = startPos.line - 1; // YAML uses 1-based lines
|
|
116
|
+
const startCol = startPos.col - 1; // YAML uses 1-based columns
|
|
117
|
+
const endPos = err.linePos.length >= 2 ? err.linePos[1] : undefined;
|
|
118
|
+
const endLine = endPos ? endPos.line - 1 : startLine;
|
|
119
|
+
const endCol = endPos ? endPos.col - 1 : startCol + 1;
|
|
120
|
+
return Range.create(
|
|
121
|
+
Position.create(startLine, startCol),
|
|
122
|
+
Position.create(endLine, endCol)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return Range.create(Position.create(0, 0), Position.create(0, 1));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Clears diagnostics for a manifest file.
|
|
130
|
+
* Call this when the file is closed or deleted.
|
|
131
|
+
*/
|
|
132
|
+
async clearDiagnostics(manifestUri: string): Promise<void> {
|
|
133
|
+
if (!this.connection) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await this.connection.sendDiagnostics({
|
|
138
|
+
uri: manifestUri,
|
|
139
|
+
diagnostics: []
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Converts a ManifestDiagnostic to an LSP Diagnostic.
|
|
145
|
+
*/
|
|
146
|
+
private toVSCodeDiagnostic(
|
|
147
|
+
diag: ManifestDiagnostic,
|
|
148
|
+
yamlDoc: YAMLDocument.Parsed
|
|
149
|
+
): Diagnostic {
|
|
150
|
+
const range = this.findRangeForPath(diag.path, yamlDoc);
|
|
151
|
+
|
|
152
|
+
let message = diag.message;
|
|
153
|
+
if (diag.hint) {
|
|
154
|
+
message += `\nHint: ${diag.hint}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
severity: this.toVSCodeSeverity(diag.severity),
|
|
159
|
+
range,
|
|
160
|
+
message,
|
|
161
|
+
source: 'domainlang',
|
|
162
|
+
code: diag.code
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Converts ManifestSeverity to LSP DiagnosticSeverity.
|
|
168
|
+
*/
|
|
169
|
+
private toVSCodeSeverity(severity: ManifestSeverity): DiagnosticSeverity {
|
|
170
|
+
switch (severity) {
|
|
171
|
+
case 'error':
|
|
172
|
+
return DiagnosticSeverity.Error;
|
|
173
|
+
case 'warning':
|
|
174
|
+
return DiagnosticSeverity.Warning;
|
|
175
|
+
case 'info':
|
|
176
|
+
return DiagnosticSeverity.Information;
|
|
177
|
+
default:
|
|
178
|
+
return DiagnosticSeverity.Warning;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Finds the source range for a YAML path like "dependencies.core.version".
|
|
184
|
+
* Returns a fallback range at start of file if path not found.
|
|
185
|
+
*/
|
|
186
|
+
private findRangeForPath(path: string, yamlDoc: YAMLDocument.Parsed): Range {
|
|
187
|
+
const fallback = Range.create(Position.create(0, 0), Position.create(0, 1));
|
|
188
|
+
|
|
189
|
+
if (!yamlDoc.contents || !isMap(yamlDoc.contents)) {
|
|
190
|
+
return fallback;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const parts = path.split('.');
|
|
194
|
+
let currentNode: unknown = yamlDoc.contents;
|
|
195
|
+
|
|
196
|
+
for (const part of parts) {
|
|
197
|
+
if (!isMap(currentNode)) {
|
|
198
|
+
return fallback;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const mapNode = currentNode as YAMLMap;
|
|
202
|
+
const item = mapNode.items.find((pair): pair is Pair =>
|
|
203
|
+
isPair(pair) && isScalar(pair.key) && String(pair.key.value) === part
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!item) {
|
|
207
|
+
return fallback;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// If this is the last part, return the range of the key
|
|
211
|
+
if (part === parts[parts.length - 1]) {
|
|
212
|
+
const keyNode = item.key;
|
|
213
|
+
if (isScalar(keyNode) && keyNode.range) {
|
|
214
|
+
const [start, end] = keyNode.range;
|
|
215
|
+
return this.offsetsToRange(start, end, yamlDoc.toString());
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
currentNode = item.value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return fallback;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Converts byte offsets to a VS Code Range using line/column calculation.
|
|
227
|
+
*/
|
|
228
|
+
private offsetsToRange(startOffset: number, endOffset: number, content: string): Range {
|
|
229
|
+
const lines = content.split('\n');
|
|
230
|
+
let currentOffset = 0;
|
|
231
|
+
let startLine = 0;
|
|
232
|
+
let startCol = 0;
|
|
233
|
+
let endLine = 0;
|
|
234
|
+
let endCol = 0;
|
|
235
|
+
let foundStart = false;
|
|
236
|
+
let foundEnd = false;
|
|
237
|
+
|
|
238
|
+
for (let lineNum = 0; lineNum < lines.length && !foundEnd; lineNum++) {
|
|
239
|
+
const lineLength = lines[lineNum].length + 1; // +1 for newline
|
|
240
|
+
|
|
241
|
+
if (!foundStart && currentOffset + lineLength > startOffset) {
|
|
242
|
+
startLine = lineNum;
|
|
243
|
+
startCol = startOffset - currentOffset;
|
|
244
|
+
foundStart = true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (!foundEnd && currentOffset + lineLength >= endOffset) {
|
|
248
|
+
endLine = lineNum;
|
|
249
|
+
endCol = endOffset - currentOffset;
|
|
250
|
+
foundEnd = true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
currentOffset += lineLength;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return Range.create(
|
|
257
|
+
Position.create(startLine, startCol),
|
|
258
|
+
Position.create(endLine, endCol)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Singleton instance for use across the language server.
|
|
265
|
+
*/
|
|
266
|
+
let manifestDiagnosticsService: ManifestDiagnosticsService | undefined;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Gets or creates the manifest diagnostics service singleton.
|
|
270
|
+
*/
|
|
271
|
+
export function getManifestDiagnosticsService(): ManifestDiagnosticsService {
|
|
272
|
+
if (!manifestDiagnosticsService) {
|
|
273
|
+
manifestDiagnosticsService = new ManifestDiagnosticsService();
|
|
274
|
+
}
|
|
275
|
+
return manifestDiagnosticsService;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Helper to validate a manifest URI with the given content.
|
|
280
|
+
* Convenience function for use in file watchers.
|
|
281
|
+
*/
|
|
282
|
+
export async function validateManifestFile(
|
|
283
|
+
connection: Connection,
|
|
284
|
+
manifestUri: string,
|
|
285
|
+
content: string
|
|
286
|
+
): Promise<void> {
|
|
287
|
+
const service = getManifestDiagnosticsService();
|
|
288
|
+
service.setConnection(connection);
|
|
289
|
+
await service.validateAndSendDiagnostics(manifestUri, content);
|
|
290
|
+
}
|
package/src/sdk/index.ts
CHANGED
package/src/sdk/loader-node.ts
CHANGED
|
@@ -25,14 +25,16 @@ import { isModel } from '../generated/ast.js';
|
|
|
25
25
|
import { createDomainLangServices } from '../domain-lang-module.js';
|
|
26
26
|
import type { LoadOptions, QueryContext } from './types.js';
|
|
27
27
|
import { fromModel, augmentModel } from './query.js';
|
|
28
|
+
import { ensureImportGraphFromDocument } from '../utils/import-utils.js';
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Loads a DomainLang model from a file on disk.
|
|
31
32
|
*
|
|
32
33
|
* **Node.js only** - uses file system APIs.
|
|
33
34
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
35
|
+
* Supports multi-file models with imports: all imported files are
|
|
36
|
+
* automatically loaded and linked. Use `documents` in the result
|
|
37
|
+
* to see all loaded files.
|
|
36
38
|
*
|
|
37
39
|
* @param entryFile - Path to the entry .dlang file
|
|
38
40
|
* @param options - Optional load configuration
|
|
@@ -43,13 +45,16 @@ import { fromModel, augmentModel } from './query.js';
|
|
|
43
45
|
* ```typescript
|
|
44
46
|
* import { loadModel } from '@domainlang/language/sdk/loader-node';
|
|
45
47
|
*
|
|
46
|
-
* const { query, model } = await loadModel('./domains.dlang', {
|
|
48
|
+
* const { query, model, documents } = await loadModel('./domains.dlang', {
|
|
47
49
|
* workspaceDir: process.cwd()
|
|
48
50
|
* });
|
|
49
51
|
*
|
|
52
|
+
* // Query spans all imported files
|
|
50
53
|
* for (const bc of query.boundedContexts()) {
|
|
51
54
|
* console.log(bc.name);
|
|
52
55
|
* }
|
|
56
|
+
*
|
|
57
|
+
* console.log(`Loaded ${documents.length} files`);
|
|
53
58
|
* ```
|
|
54
59
|
*/
|
|
55
60
|
export async function loadModel(
|
|
@@ -89,9 +94,19 @@ export async function loadModel(
|
|
|
89
94
|
|
|
90
95
|
// Register document and build it
|
|
91
96
|
shared.workspace.LangiumDocuments.addDocument(document);
|
|
92
|
-
await shared.workspace.DocumentBuilder.build([document], { validation:
|
|
97
|
+
await shared.workspace.DocumentBuilder.build([document], { validation: false });
|
|
98
|
+
|
|
99
|
+
// Traverse import graph to load all imported files
|
|
100
|
+
const importedUris = await ensureImportGraphFromDocument(
|
|
101
|
+
document,
|
|
102
|
+
shared.workspace.LangiumDocuments
|
|
103
|
+
);
|
|
93
104
|
|
|
94
|
-
//
|
|
105
|
+
// Build all imported documents with validation
|
|
106
|
+
const allDocuments = Array.from(shared.workspace.LangiumDocuments.all);
|
|
107
|
+
await shared.workspace.DocumentBuilder.build(allDocuments, { validation: true });
|
|
108
|
+
|
|
109
|
+
// Wait for entry document to be fully processed
|
|
95
110
|
if (document.state < DocumentState.Validated) {
|
|
96
111
|
throw new Error(`Document not fully processed: ${absolutePath}`);
|
|
97
112
|
}
|
|
@@ -112,11 +127,16 @@ export async function loadModel(
|
|
|
112
127
|
throw new Error(`Document root is not a Model: ${entryFile}`);
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
// Augment AST nodes with SDK properties
|
|
116
|
-
|
|
130
|
+
// Augment AST nodes with SDK properties for all loaded models
|
|
131
|
+
for (const doc of allDocuments) {
|
|
132
|
+
const docModel = doc.parseResult.value;
|
|
133
|
+
if (isModel(docModel)) {
|
|
134
|
+
augmentModel(docModel);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
117
137
|
|
|
118
|
-
// Collect all document URIs
|
|
119
|
-
const documentUris: URI[] = Array.from(
|
|
138
|
+
// Collect all document URIs from the import graph
|
|
139
|
+
const documentUris: URI[] = Array.from(importedUris).map(uriStr => URI.parse(uriStr));
|
|
120
140
|
|
|
121
141
|
return {
|
|
122
142
|
model,
|
package/src/sdk/types.ts
CHANGED
|
@@ -305,29 +305,6 @@ export interface RelationshipView {
|
|
|
305
305
|
readonly astNode: Relationship;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
/**
|
|
309
|
-
* Augmented BoundedContext with SDK-resolved properties.
|
|
310
|
-
* These properties are computed during model loading using precedence rules.
|
|
311
|
-
*
|
|
312
|
-
* Property names follow the PRS design: natural names that "just work" with
|
|
313
|
-
* IDE autocomplete. Use `resolved*` prefix to avoid conflicts with existing
|
|
314
|
-
* AST properties while maintaining discoverability.
|
|
315
|
-
*
|
|
316
|
-
* @deprecated Since SDK now uses module augmentation in ast-augmentation.ts,
|
|
317
|
-
* this type alias is only kept for backwards compatibility. Use BoundedContext
|
|
318
|
-
* directly after importing the SDK.
|
|
319
|
-
*/
|
|
320
|
-
export type AugmentedBoundedContext = BoundedContext;
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Augmented Domain with SDK-resolved properties.
|
|
324
|
-
*
|
|
325
|
-
* @deprecated Since SDK now uses module augmentation in ast-augmentation.ts,
|
|
326
|
-
* this type alias is only kept for backwards compatibility. Use Domain
|
|
327
|
-
* directly after importing the SDK.
|
|
328
|
-
*/
|
|
329
|
-
export type AugmentedDomain = Domain;
|
|
330
|
-
|
|
331
308
|
/**
|
|
332
309
|
* Internal index structure for O(1) lookups.
|
|
333
310
|
* Not exported from public API.
|
|
@@ -8,42 +8,12 @@
|
|
|
8
8
|
* - Version policy enforcement
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { LockFile } from './
|
|
11
|
+
import type { LockFile, DependencyTreeNode, ReverseDependency, VersionPolicy } from './types.js';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import fs from 'node:fs/promises';
|
|
14
14
|
import os from 'node:os';
|
|
15
15
|
import YAML from 'yaml';
|
|
16
|
-
|
|
17
|
-
export interface DependencyTreeNode {
|
|
18
|
-
/** Package identifier */
|
|
19
|
-
packageKey: string;
|
|
20
|
-
/** Locked version */
|
|
21
|
-
version: string;
|
|
22
|
-
/** Commit hash */
|
|
23
|
-
commit: string;
|
|
24
|
-
/** Direct dependencies */
|
|
25
|
-
dependencies: DependencyTreeNode[];
|
|
26
|
-
/** Depth in tree (0 = root) */
|
|
27
|
-
depth: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface ReverseDependency {
|
|
31
|
-
/** Package that depends on the target */
|
|
32
|
-
dependentPackage: string;
|
|
33
|
-
/** Version of the dependent package */
|
|
34
|
-
version: string;
|
|
35
|
-
/** Type: direct or transitive */
|
|
36
|
-
type: 'direct' | 'transitive';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface VersionPolicy {
|
|
40
|
-
/** Policy name: latest, stable, or specific version */
|
|
41
|
-
policy: 'latest' | 'stable' | 'pinned';
|
|
42
|
-
/** Resolved version */
|
|
43
|
-
version: string;
|
|
44
|
-
/** Available versions for policy */
|
|
45
|
-
availableVersions?: string[];
|
|
46
|
-
}
|
|
16
|
+
import { sortVersionsDescending, isPreRelease } from './semver.js';
|
|
47
17
|
|
|
48
18
|
/**
|
|
49
19
|
* Analyzes dependency relationships and provides visualization/analysis tools.
|
|
@@ -109,7 +79,7 @@ export class DependencyAnalyzer {
|
|
|
109
79
|
if (visited.has(packageKey)) {
|
|
110
80
|
return {
|
|
111
81
|
packageKey,
|
|
112
|
-
|
|
82
|
+
ref: locked.ref,
|
|
113
83
|
commit: locked.commit,
|
|
114
84
|
dependencies: [], // Don't recurse into already visited
|
|
115
85
|
depth,
|
|
@@ -133,7 +103,7 @@ export class DependencyAnalyzer {
|
|
|
133
103
|
|
|
134
104
|
return {
|
|
135
105
|
packageKey,
|
|
136
|
-
|
|
106
|
+
ref: locked.ref,
|
|
137
107
|
commit: locked.commit,
|
|
138
108
|
dependencies: children,
|
|
139
109
|
depth,
|
|
@@ -168,7 +138,7 @@ export class DependencyAnalyzer {
|
|
|
168
138
|
if (dep.source === targetPackage) {
|
|
169
139
|
reverseDeps.push({
|
|
170
140
|
dependentPackage: 'root',
|
|
171
|
-
|
|
141
|
+
ref: 'workspace',
|
|
172
142
|
type: 'direct',
|
|
173
143
|
});
|
|
174
144
|
}
|
|
@@ -190,7 +160,7 @@ export class DependencyAnalyzer {
|
|
|
190
160
|
if (packageDeps[targetPackage]) {
|
|
191
161
|
reverseDeps.push({
|
|
192
162
|
dependentPackage: packageKey,
|
|
193
|
-
|
|
163
|
+
ref: locked.ref,
|
|
194
164
|
type: 'direct',
|
|
195
165
|
});
|
|
196
166
|
}
|
|
@@ -207,11 +177,11 @@ export class DependencyAnalyzer {
|
|
|
207
177
|
|
|
208
178
|
const formatNode = (node: DependencyTreeNode, prefix: string, isLast: boolean): void => {
|
|
209
179
|
const branch = isLast ? '└── ' : '├── ';
|
|
210
|
-
const
|
|
211
|
-
? `${node.
|
|
212
|
-
: node.
|
|
180
|
+
const refStr = options.showCommits
|
|
181
|
+
? `${node.ref} (${node.commit.substring(0, 7)})`
|
|
182
|
+
: node.ref;
|
|
213
183
|
|
|
214
|
-
lines.push(`${prefix}${branch}${node.packageKey}@${
|
|
184
|
+
lines.push(`${prefix}${branch}${node.packageKey}@${refStr}`);
|
|
215
185
|
|
|
216
186
|
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
217
187
|
node.dependencies.forEach((child, index) => {
|
|
@@ -273,38 +243,38 @@ export class DependencyAnalyzer {
|
|
|
273
243
|
}
|
|
274
244
|
|
|
275
245
|
/**
|
|
276
|
-
* Resolves
|
|
246
|
+
* Resolves ref policies (latest, stable) to concrete refs.
|
|
277
247
|
*/
|
|
278
248
|
async resolveVersionPolicy(
|
|
279
|
-
|
|
249
|
+
_packageKey: string,
|
|
280
250
|
policy: string,
|
|
281
|
-
|
|
251
|
+
availableRefs: string[]
|
|
282
252
|
): Promise<VersionPolicy> {
|
|
283
253
|
if (policy === 'latest') {
|
|
284
|
-
// Return the most recent
|
|
285
|
-
const sorted =
|
|
254
|
+
// Return the most recent ref
|
|
255
|
+
const sorted = sortVersionsDescending(availableRefs);
|
|
286
256
|
return {
|
|
287
257
|
policy: 'latest',
|
|
288
|
-
|
|
289
|
-
|
|
258
|
+
ref: sorted[0] || 'main',
|
|
259
|
+
availableRefs: sorted,
|
|
290
260
|
};
|
|
291
261
|
}
|
|
292
262
|
|
|
293
263
|
if (policy === 'stable') {
|
|
294
|
-
// Return the most recent stable
|
|
295
|
-
const stable =
|
|
296
|
-
const sorted =
|
|
264
|
+
// Return the most recent stable ref (exclude pre-release)
|
|
265
|
+
const stable = availableRefs.filter(v => !isPreRelease(v));
|
|
266
|
+
const sorted = sortVersionsDescending(stable);
|
|
297
267
|
return {
|
|
298
268
|
policy: 'stable',
|
|
299
|
-
|
|
300
|
-
|
|
269
|
+
ref: sorted[0] || 'main',
|
|
270
|
+
availableRefs: sorted,
|
|
301
271
|
};
|
|
302
272
|
}
|
|
303
273
|
|
|
304
|
-
// Pinned
|
|
274
|
+
// Pinned ref
|
|
305
275
|
return {
|
|
306
276
|
policy: 'pinned',
|
|
307
|
-
|
|
277
|
+
ref: policy,
|
|
308
278
|
};
|
|
309
279
|
}
|
|
310
280
|
|
|
@@ -348,34 +318,4 @@ export class DependencyAnalyzer {
|
|
|
348
318
|
return {};
|
|
349
319
|
}
|
|
350
320
|
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Sorts versions in descending order (newest first).
|
|
354
|
-
*/
|
|
355
|
-
private sortVersions(versions: string[]): string[] {
|
|
356
|
-
return versions.sort((a, b) => {
|
|
357
|
-
// Simple lexicographic sort (good enough for basic semver)
|
|
358
|
-
// Production: use semver library
|
|
359
|
-
const aParts = a.replace(/^v/, '').split('.').map(Number);
|
|
360
|
-
const bParts = b.replace(/^v/, '').split('.').map(Number);
|
|
361
|
-
|
|
362
|
-
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
363
|
-
const aNum = aParts[i] || 0;
|
|
364
|
-
const bNum = bParts[i] || 0;
|
|
365
|
-
if (aNum !== bNum) {
|
|
366
|
-
return bNum - aNum; // Descending
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return 0;
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Checks if a version is a pre-release.
|
|
376
|
-
*/
|
|
377
|
-
private isPreRelease(version: string): boolean {
|
|
378
|
-
const clean = version.replace(/^v/, '');
|
|
379
|
-
return /-(alpha|beta|rc|pre|dev)/.test(clean);
|
|
380
|
-
}
|
|
381
321
|
}
|